Python面向对象编程思想
分类:计算机编程

Python中的类(一)

1.面向过程编程:计算机通过一系列指令来一步一步完成任务。

面向对象编程——Object Oriented Programming,简称OOP,是一种程序设计思想。OOP把对象作为程序的基本单元,一个对象包含了数据和操作数据的函数。

一、 应用场景

如果多个函数中有一些相同的参数时,转换成面向对象。

2.面向对象编程(oop):通过“类”和“对象”来创建模型,用于对真实世界进行描述。

面向过程的程序设计把计算机程序视为一系列的命令集合,即一组函数的顺序执行。为了简化程序设计,面向过程把函数继续切分为子函数,即把大块函数通过切割成小块函数来降低系统的复杂度。

二、 如何创建类

类是用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例。

Class 类名:

       Pass

3.Class 类
一个类即是对一类拥有相同属性的对象的抽象、蓝图、原型。在类中定义了这些对象都具备的属性(variables(data))和相同的方法 。

而面向对象的程序设计把计算机程序视为一组对象的集合,而每个对象都可以接收其他对象发过来的消息,并处理这些消息,计算机程序的执行就是一系列消息在各个对象之间传递。

三、 类变量

类变量在整个实例化的对象中是公用的。类变量定义在类中且在函数体之外。类变量通常不作为实例变量使用。

4.Object 对象 

在Python中,所有数据类型都可以视为对象,当然也可以自定义对象。自定义的对象数据类型就是面向对象中的类(Class)的概念。

四、 创建类中的方法

方法是类中定义的函数。

一个对象即是一个类的实例化后实例,一个类必须经过实例化后方可在程序中调用,一个类可以实例化多个对象,每个对象亦可以有不同的属性,就像人类是指所有人,每个人是指具体的对象,人与人之前有共性,亦有不同

我们以一个例子来说明面向过程和面向对象在程序流程上的不同之处。

1、普通法

Obj = 类名 ()

Obj . 普通方法名

5.Encapsulation 封装
在类中对数据的赋值、内部调用对外部用户是透明的,这使类变成了一个胶囊或容器,里面包含着类的数据和方法

假设我们要处理学生的成绩表,为了表示一个学生的成绩,面向过程的程序可以用一个dict表示:

2、设定初始化(构造方法、封装特性)

由于类可以起到模板的作用,因此,可以在创建实例的时候,把一些我们认为必须绑定的属性强制填写进去。通过定义一个特殊的__init__方法,在创建实例的时候,就把name,score等属性绑上去:

class Student(object):

    def __init__(self, name, score):

        self.name = name

        self.score = score

 注意:特殊方法“init”前后有两个下划线!!!

注意到__init__方法的第一个参数永远是self,表示创建的实例本身,因此,在__init__方法内部,就可以把各种属性绑定到self,因为self就指向创建的实例本身。

有了__init__方法,在创建实例的时候,就不能传入空的参数了,必须传入与__init__方法匹配的参数,但self不需要传,Python解释器自己会把实例变量传进去:

>>> bart = Student('Bart Simpson', 59)

>>> bart.name

'Bart Simpson'

>>> bart.score

59

和普通的函数相比,在类中定义的函数只有一点不同,就是第一个参数永远是实例变量self,并且,调用时,不用传递该参数。除此之外,类的方法和普通函数没有什么区别,所以,你仍然可以用默认参数、可变参数、关键字参数和命名关键字参数。

6.Inheritance 继承
一个类可以派生出子类,在这个父类里定义的属性、方法自动被子类继承

std1 ={ 'name': 'Michael', 'score':98}

五、继承特性

 继承是为了使用父类中的方法。

创建实例:

class Father():             #父类

          def f1(self):

                 print (‘父法一’)

class Son(Father):             #子类

          def s1(self):

                 print(‘子法一’)

调用:

  • obj=Son()
  • obj.s1()
  • obj.f1()

#执行结果:子法一

                    父法一

当子类中的方法名和父类的方法名一致时(重写父类的某方法),调用该方法时,执行的是子类中的方法。重写时,还想要执行父类的方法时可以参照以下方法。

class Father():             #父类

          def f1(self):

                 print (‘父法一’)

class Son(Father):             #子类

          def s1(self):

                 super(Son,self).f1           #参数:子类名,self

                 print(‘子法一’)

#调用

  • obj=Son()
  • obj.s1()

#执行结果:父法一

                    子法一

也可以按照以下方法写:

class Father():             #父类

          def f1(self):

                 print (‘父法一’)

class Son(Father):             #子类

          def s1(self):

                 Father.f1(self)

                 print(‘子法一’)

#调用

  • obj=Son()
  • obj.s1()

多继承:

当需要一个子类继承多个父类时,可以参照以下代码:

class Father1():           #父类一

          def f1(self):

                 print (‘父类一’)

class Father2():           #父类二

          def f2(self):

                 print (‘父类二’)

class Son(Father1,Father2):              #子类

          def s1(self):

                            print(‘子法一’)

继承父类的顺序是从左到右继承。即,当继承的多个父类中有相同名称的方法,在调用时会执行左侧父类的方法,而右侧父类的方法则不会执行。这与执行顺序有关。当调用子类时,程序会先从子类的方法中匹配,如果子类中没有则去符类中依次匹配,父类的匹配顺序是从左到右。

当多父类继承共同继承一个祖先类,而调用的方法在祖先类时,的查找调用顺序如下图。

图片 1

当调用父类的方法中又调用了其他方法,则会从子类开始匹配查找该方法。即使,原先的父类中有该方法,也会从子类查找。

7.Polymorphism 多态
态是面向对象的重要特性,简单点说:“一个接口,多种实现”,指一个基类中派生出了不同的子类,且每个子类在继承了同样的方法名的同时又对父类的方法做了不同的实现,这就是同一种事物表现出的多种形态。
编程其实就是一个将具体世界进行抽象化的过程,多态就是抽象化的一种体现,把一系列具体事物的共同点抽象出来, 再通过这个抽象的事物, 与不同的具体事物进行对话。
对不同类的对象发出相同的消息将会有不同的行为。比如,你的老板让所有员工在九点钟开始工作, 他只要在九点钟的时候说:“开始工作”即可,而不需要对销售人员说:“开始销售工作”,对技术人员说:“开始技术工作”, 因为“员工”是一个抽象的事物, 只要是员工就可以开始工作,他知道这一点就行了。至于每个员工,当然会各司其职,做各自的工作。
多态允许将子类的对象当作父类的对象使用,某父类型的引用指向其子类型的对象,调用的方法是该子类型的方法。这里引用和调用方法的代码编译前就已经决定了,而引用所指向的对象可以在运行期间动态绑定

std2 ={ 'name': 'Bob', 'score':81}

8.无论用什么形式来编程,我们都要明确记住以下原则:

而处理学生成绩可以通过函数实现,比如打印学生的成绩:

  1. 写重复代码是非常不好的低级行为
  2. 你写的代码需要经常变更 

def print_score(std):

9.类的定义

print('%s: %s' % (std['name'], std['score']))

新式类:class Role(object):  推荐用新式类

如果采用面向对象的程序设计思想,我们首选思考的不是程序的执行流程,而是Student这种数据类型应该被视为一个对象,这个对象拥有name和score这两个属性(Property)。如果要打印一个学生的成绩,首先必须创建出这个学生对应的对象,然后,给对象发一个print_score消息,让对象自己把自己的数据打印出来。

经典类:class Role:

class Student(object):

新式类与继承类的区别就是子类在继承父类的时候,括号内父类顺序可以随便写:class Son3(Father,jiao_you): 

def __init__(self, name, score):

类中有四个重要点:私有属性、私有函数、继承、多态

self.name = name

class Role:

self.score = score    def print_score(self):

  n = 123   #名称【类变量】,用不到可不写,存在类的内存里,而不是实例的内存里【注意:如果实例里也有n,则实例在调用n的时候就会调用实例里的n,遵循就近化原则】

print('%s: %s' % (self.name, self.score))

                       如果n不是列表,则在某一实例中修改n的值,只会修改该实例中n的值,而类的n和其他实例的n都不会变

给对象发消息实际上就是调用对象对应的关联函数,我们称之为对象的方法(Method)。面向对象的程序写出来就像这样:

                       如果n是列表,则在某一实例中修改n的值,则会修改该实例中n的值,而类的n和其他实例的n都会被修改,因为他们使用的都是同一个列表**

bart = Student('Bart Simpson', 59)

  def __init__(self,name,role)   #实例化的时候(r1 = Role()),用到self,这里的self指代实例名r1,若r2= Role(),则self指代r2.

lisa = Student('Lisa Simpson', 87)

    #__init__函数的作用:它就是构造函数,即初始化函数(与之对应的是析构函数),就是在将类实例化的时候(r1 = Role()),在内存里开辟一块内存,内存里存放了定义的变量,方便r1使用

bart.print_score()

    self.name = name  #(name为实例变量【又称静态属性】,实例变量只能作用于实例本身)#r1 = Role()  ======>>   r1 = Role(r1,name,role) 【俩r1不一样,python直接将实例名作为对象名】=======>>  为了这个类 能给其他实例用,所以用self指代第二个r1

lisa.print_score()

           self.role = role             

面向对象的设计思想是从自然界中来的,因为在自然界中,类(Class)和实例(Instance)的概念是很自然的。Class是一种抽象概念,比如我们定义的Class——Student,是指学生这个概念,而实例(Instance)则是一个个具体的Student,比如,Bart Simpson和Lisa Simpson是两个具体的Student。

       self.__age = age  #私有属性,(__表示私有)实例的私有属性不能被外部函数或其他实例修改,就算实例自己直接调用也不行(如:print(r1.__age) 会报错),只能再在类里定义一个函数,让该函数查看或修改私有属性,self.__age。

所以,面向对象的设计思想是抽象出Class,根据Class创建Instance。

  def __del__(self):       #__del__函数就是析构函数,它会在最后一个该类的实例运行结束后自动执行,用来关闭打开的临时文件等,回收内存。但是在执行del r1(删除r1实例)语句后,析构函数也会执行。

面向对象的抽象程度又比函数要高,因为一个Class既包含数据,又包含操作数据的方法。

    print("%s I am gone!" %self.name)

小结

  def  hehe(self,name):                  #定义的函数【又称动态属性】,在定义函数的时候,每个函数都必须加上self

数据封装、继承和多态是面向对象的三大特点 。

    print("%s 呵呵" % self.name)

类(Class)和实例(Instance)

  def __hello(self):   #私有函数,(__表示私有)实例的私有函数不能被外部函数或其他实例调用,就算是实例自己直接调用也不行(如:r1.__hello() 会报错),只能再在类里定义一个函数,让该函数调用私有函数,调用的时候要用这样     self.__hello()    。

面向对象最重要的概念就是类(Class)和实例(Instance),必须牢记类是抽象的模板,比如Student类,而实例是根据类创建出来的一个个具体的“对象”,每个对象都拥有相同的方法,但各自的数据可能不同。

    print("hello! %s" % self.name)

仍以Student类为例,在Python中,定义类是通过class关键字:

r1 = Role('wt','student')    #实例初始化,括号里的是默认值

class Student(object):

r1.属性                           (如下,调用类中的函数或类变量)

pass

r1.add_other = 'I am other'  #给实例添加额外属性【实例属性的增删改查只作用于本实例,不作用于另一个实例,更不作用于类】

class后面紧接着是类名,即Student,类名通常是大写开头的单词,紧接着是(object),表示该类是从哪个类继承下来的,继承的概念我们后面再讲,通常,如果没有合适的继承类,就使用object类,这是所有类最终都会继承的类。

r1.name = 'eric'                     #修改实例默认值

定义好了Student类,就可以根据Student类创建出Student的实例,创建实例是通过类名 ()实现的:

del r1.name                          #删除实例的name属性,并不会删除类的name属性

>>> bart = Student()

r1.hehe('wt')                    #调用hehe函数

>>> bart<__main__.Studentobjectat0x10a67a590>

输出:wt 呵呵

>>> Student

10.继承

可以看到,变量bart指向的就是一个Student的实例,后面的0x10a67a590是内存地址,每个object的地址都不一样,而Student本身则是一个类。

class Father(object):   #定义一个父类

可以自由地给一个实例变量绑定属性,比如,给实例bart绑定一个name属性:

  def __init__(self,name,age):

>>>bart.name ='Bart Simpson'

    self.name = name

>>>bart.name'Bart Simpson'

    self.age = age

由于类可以起到模板的作用,因此,可以在创建实例的时候,把一些我们认为必须绑定的属性强制填写进去。通过定义一个特殊的__init__方法,在创建实例的时候,就把name,score等属性绑上去:

  def eat(self):

class Student(object):

    print("%s is eating" % self.name)

def __init__(self, name, score):

  def sleep(self):

self.name = name

    print("%s is sleeping" % self.name)

self.score = score

class Jiao_you(object):            #这个父类是用来让子类多继承用的,不需要__init__函数

注意:特殊方法“init”前后有两个下划线!!!

  def make_friends(self,obj):  #obj参数是指代对象变量,而不是普通变量

注意到__init__方法的第一个参数永远是self,表示创建的实例本身,因此,在__init__方法内部,就可以把各种属性绑定到self,因为self就指向创建的实例本身。

    print("%s make friends with %s" %(self.name,obj.name))   #这里的name变量是子类的name变量,只有在对子类实例化后才生效

有了__init__方法,在创建实例的时候,就不能传入空的参数了,必须传入与__init__方法匹配的参数,但self不需要传,Python解释器自己会把实例变量传进去:

 

>>>bart = Student('Bart Simpson',59)

class Son(Father): #定义一个子类,单继承

>>>bart.name'Bart Simpson'

  def new(self):        #定义一个新函数new(),相对父类来说,又称函数重构

>>>bart.score59

    Father.eat(self) #可以这样调用父类的函数

和普通的函数相比,在类中定义的函数只有一点不同,就是第一个参数永远是实例变量self,并且,调用时,不用传递该参数。除此之外,类的方法和普通函数没有什么区别,所以,你仍然可以用默认参数、可变参数、关键字参数和命名关键字参数。

class Son2(Father): #定义另一个子类,想让这个子类增加新功能

数据封装

  def __init__(self,,name,age,money):  #money为新增加的功能

面向对象编程的一个重要特点就是数据封装。在上面的Student类中,每个实例就拥有各自的name和score这些数据。我们可以通过函数来访问这些数据,比如打印一个学生的成绩:

    Father.__init__(self,name,age)       #先调用父类的__init__()函数

>>>defprint_score(std):...print('%s: %s'% (std.name, std.score))

           等同于super(Son2,self).init(name,age)  #推荐用super(),也是先调用父类的__init__()函数,但是它会自己分配继承顺序

...

    self.money = money                     #再初始化新增加的变量

>>>print_score(bart)

  def hello(self):        #定义一个新函数hello()

Bart Simpson:59

    Father.eat(self) #可以这样调用父类的函数

但是,既然Student实例本身就拥有这些数据,要访问这些数据,就没有必要从外面的函数去访问,可以直接在Student类的内部定义访问数据的函数,这样,就把“数据”给封装起来了。这些封装数据的函数是和Student类本身是关联起来的,我们称之为类的方法:

    print("花费:%s" % self.money)

class Student(object):

 

def __init__(self, name, score):

class Son3(Father,jiao_you): #定义第三个子类,这是多继承

self.name = name

  def new(self):        #定义一个新函数new(),相对父类来说,又称函数重构

self.score = score    def print_score(self):

    Father.eat(self) #可以这样调用父类的函数

print('%s: %s' % (self.name, self.score))

  

要定义一个方法,除了第一个参数是self外,其他和普通函数一样。要调用一个方法,只需要在实例变量上直接调用,除了self不用传递,其他参数正常传入:

      

>>> bart.print_score()

son1 = Son("Alex",50)  #将Son类实例化为son1

Bart Simpson: 59

son1.sleep()    #输出:Alex is sleeping

这样一来,我们从外部看Student类,就只需要知道,创建实例需要给出name和score,而如何打印,都是在Student类的内部定义的,这些数据和逻辑被“封装”起来了,调用很容易,但却不用知道内部实现的细节。

son1.new()     #输出:Alex is eating

封装的另一个好处是可以给Student类增加新的方法,比如get_grade:

son_Alex = Son3('Alex',50)  #将Son3类实例化为son_Alex

class Student(object):

son_Eric = Son3('Eric',20)   #将Son3类实例化为son_Eric

...    def get_grade(self):

son_Alex.make_friends(son_Eric)    #输出:Alex make friends with Eric     这里的son_Eric实例就是父类函数make_friends(self,obj)的参数obj

if self.score >= 90:            return 'A'

11.组合:

elif self.score >= 60:            return 'B'

         class A()**:**

else:            return 'C'

         class B():

同样的,get_grade方法可以直接在实例变量上调用,不需要知道内部实现细节:

      def __init__(A):   将类A作为一个对象传给类B,写的时候,只需要写A的名字即可,不用写它的参数,调用的时候直接用  self.t.函数  即可。

>>> bart.get_grade()'C'

        self.t = A

小结

12.继承顺序

类是创建实例的模板,而实例则是一个一个具体的对象,各个实例拥有的数据都互相独立,互不影响;

(1)广度优先      在python3中是按广度优先继承的

方法就是与实例绑定的函数,和普通函数不同,方法可以直接访问实例的数据;

class B(A):    B继承A

通过在实例上调用方法,我们就直接操作了对象内部的数据,但无需知道方法内部的实现细节。

class C(A):   C继承A

和静态语言不同,Python允许对实例变量绑定任何数据,也就是说,对于两个实例变量,虽然它们都是同一个类的不同实例,但拥有的变量名称都可能不同:

class Son3(B,C):   #子类Son3先继承B,找不到B再找C,最后再找A

>>> bart = Student('Bart Simpson', 59)

(2)深度优先    在python2中经典类是按深度优先继承的,新式类是按广度优先继承的

>>> lisa = Student('Lisa Simpson', 87)

class B(A):   B继承A

>>> bart.age = 8

class C(A):   C继承A

>>> bart.age

class Son3(B,C):   #子类Son3先继承B,找不到B再找A

8

13.多态

>>> lisa.age

多态性(polymorphisn)是允许你将父对象设置成为和一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。简单的说,就是一句话:允许将子类类型的指针赋值给父类类型的指针。

Traceback (most recent call last):

那么,多态的作用是什么呢?我们知道,封装可以隐藏实现细节,使得代码模块化;继承可以扩展已存在的代码模块(类);它们的目的都是为了——代码重用。而多态则是为了实现另一个目的——接口重用!多态的作用,就是为了类在继承和派生的时候,保证使用“家谱”中任一类的实例的某一属性时的正确调用。

File "", line 1, in AttributeError: 'Student' object has no attribute 'age'

 

本文转自python工程师玫瑰,感谢!

Pyhon不直接支持多态,但可以间接实现

 

通过Python模拟的多态

 1 class Animal:
 2     def __init__(self, name):    # Constructor of the class
 3         self.name = name
 4     def talk(self):              # Abstract method, defined by convention only
 5         raise NotImplementedError("Subclass must implement abstract method")
 6  
 7 class Cat(Animal):
 8     def talk(self):
 9         return 'Meow!'
10  
11 class Dog(Animal):
12     def talk(self):
13         return 'Woof! Woof!'
14  
15 animals = [Cat('Missy'),
16            Dog('Lassie')]
17  
18 for animal in animals:
19     print animal.name   ': '   animal.talk()

 

 

本文由pc28.am发布于计算机编程,转载请注明出处:Python面向对象编程思想

上一篇:reactor使用教程,学习系列 下一篇:没有了
猜你喜欢
热门排行
精彩图文
  • Python面向对象编程思想
    Python面向对象编程思想
    Python中的类(一) 1.面向过程编程:计算机通过一系列指令来一步一步完成任务。 面向对象编程——Object OrientedProgramming,简称OOP,是一种程序设计思想。
  • Pycharm的安装和使用,Adelaide装修网深入分析厨房
    Pycharm的安装和使用,Adelaide装修网深入分析厨房
      MapServer linux上服务安装 关于厨房中水管的安装常见的就是下水管的安装,对于下水管的安装可能很多人都不知道该如何安装,青岛装修网资深装修达人说
  • 电子商务货品库的成品设计,PHP数组内容不重复
    电子商务货品库的成品设计,PHP数组内容不重复
    多年来在做ecshop的货物仓库储存模块,分别给黄金年代款商品的两性情格组合设置仓库储存,如下图: # 手艺文书档案 每一天逛天猫和京东的时候,映着重
  • 九彩拼盘的前端技能,LayUI框架的应用
    九彩拼盘的前端技能,LayUI框架的应用
    内容: HTML 普及标签和总体性 文书档案类型申明 转义字符 网页访问无障碍(只是掌握卡塔 尔(阿拉伯语:قطر‎ CSS 常用采取器 体制生效准绳(浏览器的
  • 编制程序总计,动态目的
    编制程序总计,动态目的
    dynamic是FrameWork4.0的新特色。dynamic的现身让C#具备了弱语言类型的风味。编写翻译器在编写翻译的时候不再对项目举行检查,编译期暗中同意dynamic对象扶植