本节内容:
引子
面向对象 v.s. 面向过程
面向对象编程介绍
面向对象的特性:
      封装
      继承
      多态
类、方法
 

1、引子

假设你现在是一家游戏公司的开发人员,现在需要你开发一款叫做<人狗大战>的游戏,你就思考呀,人狗作战,那至少需要2个角色,一个是人, 一个是狗,且人和狗都有不同的技能,比如人拿棍打狗, 狗可以咬人,怎么描述这种不同的角色和他们的功能呢?
你搜罗了自己掌握的所有技能,写出了下面的代码来描述这两个角色:
def person(name,age,sex,job):data = {'name':name,'age':age,'sex':sex,'job':job} return datadef dog(name,dog_type):data = {'name':name,'type':dog_type}return data

上面两个方法相当于造了两个模子,游戏开始,你得生成一个人和狗的实际对象吧,怎么生成呢?

d1 = dog("李磊","京巴")p1 = person("严帅",36,"F","运维")p2 = person("林海峰",27,"F","Teacher")

两个角色对象生成了,狗和人还有不同的功能呀,狗会咬人,人会打狗,对不对? 怎么实现呢?想到了, 可以每个功能再写一个函数,想执行哪个功能,直接 调用 就可以了,对不?

def bark(d):print("dog %s:wang.wang..wang..."%d['name']) def walk(p):print("person %s is walking..." %p['name'])

d1 = dog("李磊","京巴")
p1 = person("严帅",36,"F","运维")
p2 = person("林海峰",27,"F","Teacher")walk(p1)
bark(d1)

但是仔细玩耍一会,你就不小心干了下面这件事:

p1 = person("严帅",36,"F","运维")
bark(p1)         # 把人的对象传给了狗的方法, 人家不干了

事实 上,这并没出错。很显然,人是不能调用狗的功能的,如何在代码级别实现这个限制呢?

 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 # Author: antcolonies
 4
 5 def person(name, age, sex, job):
 6     def walk(p):
 7         print("person %s is walking..." % p['name'])
 8     data = {
 9         'name': name,
10         'age': age,
11         'sex': sex,
12         'job': job,
13         'walk': walk
14     }
15     return data
16
17
18 def dog(name, dog_type):
19     def bark(d):
20         print("dog %s:wang.wang..wang..." % d['name'])
21     data = {
22         'name': name,
23         'type': dog_type,
24         'bark': bark
25     }
26     return data
27
28 d1 = dog("李磊","京巴")
29 p1 = person("严帅",36,"F","运维")
30 p2 = person("林海峰",27,"F","Teacher")
31
32 d1['bark'](p1)        # 把人这个对象传给了狗

View Code

你是如此的机智,这样就实现了限制人只能用人自己的功能啦。

但,我的哥,不要高兴太早,刚才你只是阻止了两个完全 不同的角色 之前的功能混用, 但有没有可能 ,同一个种角色,但有些属性是不同的呢? 比如 ,大家都打过cs吧,cs里有警察和恐怖份子,但因为都 是人, 所以你写一个角色叫 person(), 警察和恐怖份子都 可以 互相射击,但警察不可以杀人质,恐怖分子可以,这怎么实现呢? 你想了说想,说,简单,只需要在杀人质的功能里加个判断,如果是警察,就不让杀不就ok了么。 没错, 这虽然 解决了杀人质的问题,但其实你会发现,警察和恐怖分子的区别还有很多,同时又有很多共性,如果 在每个区别处都 单独做判断,那得累死。

你想了想说, 那就直接写2个角色吧, 反正 这么多区别, 我的哥, 不能写两个角色呀,因为他们还有很多共性 , 写两个不同的角色,就代表 相同的功能 也要重写了,是不是我的哥? 。。。

好了, 话题就给你点到这, 再多说你的智商 也理解不了了!

2、面向对象 v.s. 面向过程

编程范式

编程是 程序 员 用特定的语法+数据结构+算法组成的代码来告诉计算机如何执行任务的过程 , 一个程序是程序员为了得到一个任务结果而编写的一组指令的集合,正所谓条条大路通罗马,实现一个任务的方式有很多种不同的方式, 对这些不同的编程方式的特点进行归纳总结得出来的编程方式类别,即为编程范式。 不同的编程范式本质上代表对各种类型的任务采取的不同的解决问题的思路, 大多数语言只支持一种编程范式,当然也有些语言可以同时支持多种编程范式。 两种最重要的编程范式分别是面向过程编程和面向对象编程。

面向过程编程(Procedural Programming)
Procedural programming uses a list of instructions to tell the computer what to do step-by-step.
面向过程编程依赖 - 你猜到了- procedures,一个procedure包含一组要被进行计算的步骤, 面向过程又被称为top-down languages, 就是程序从上到下一步步执行,一步步从上到下,从头到尾的解决问题 。基本设计思路就是程序一开始是要着手解决一个大的问题,然后把一个大问题分解成很多个小问题或子过程,这些子过程再执行的过程再继续分解直到小问题足够简单到可以在一个小步骤范围内解决。

举个典型的面向过程的例子, 数据库备份, 分三步,连接数据库,备份数据库,测试备份文件可用性。

代码如下:

def db_conn():print("connecting db...") def db_backup(dbname):print("导出数据库...",dbname)print("将备份文件打包,移至相应目录...")def db_backup_test():print("将备份文件导入测试库,看导入是否成功") def main():db_conn()db_backup('my_db')db_backup_test()  if __name__ == '__main__':main()

这样做的问题也是显而易见的,就是如果你要对程序进行修改,对你修改的那部分有依赖的各个部分你都也要跟着修改, 举个例子,如果程序开头你设置了一个变量值为1 , 但如果其它子过程依赖这个值为1的变量才能正常运行,那如果你改了这个变量,那这个子过程你也要修改,假如又有一个其它子程序依赖这个子过程,那就会发生一连串的影响,随着程序越来越大, 这种编程方式的维护难度会越来越高。
所以我们一般认为, 如果你只是写一些简单的脚本,去做一些一次性任务,用面向过程的方式是极好的,但如果你要处理的任务是复杂的,且需要不断迭代和维护 的, 那还是用面向对象最方便了。

面向对象编程

OOP编程是利用“类”和“对象”来创建各种模型来实现对真实世界的描述,使用面向对象编程的原因一方面是因为它可以使程序的维护和扩展变得更简单,并且可以大大提高程序开发效率 ,另外,基于面向对象的程序可以使它人更加容易理解你的代码逻辑,从而使团队开发变得更从容。

面向对象的几个核心特性如下

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

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

Encapsulation 封装
在类中数据的赋值或内部调用等操作对外部用户来说是透明的,这使类变成了一个胶囊或容器,里面包含着类的属性和方法。

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

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

3、面向对象编程(Object-Oriented Programming )介绍

类的语法

class Dog(object): print("hello,I am a dog!")d = Dog()    # 实例化这个类,此时的d就是类Dog的一个实例化对象# 实例化,其实就是以Dog类为模版,在内存里开辟一块空间,存上数据,赋值成一个变量名

上面的代码其实有问题,想给狗起名字传不进去。

class Dog(object):       # 新式类def __init__(self,name,dog_type):self.name = nameself.type = dog_typedef sayhi(self): print("hello,I am a dog, my name is ",self.name)d = Dog('LiChuang',"京巴")
d.sayhi()

类的实例化示意图:

根据上图我们得知,其实self,就是实例本身!你实例化时python会自动把这个实例本身通过self参数传进去。

首先来看一下以下代码,理解一些概念(新式类/经典类,类变量/实例变量,私有属性/私有方法,构造方法/析构方法):

 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 # Author: antcolonies
 4
 5 # class Role(object):  新式类
 6 class Role:       # 经典类
 7     n = 123
 8     n_list = []
 9     Name = '我是类name'
10     name = 'Class name'
11     '''
12     以上变量n, n_list, Name, name均为类变量
13     '''
14
15     def __init__(self, name, role, weapon, life_value=100,money=15000):
16         '''__init__()为构造函数/方法,用于初次调用时的变量等的初始化'''
17         self.name = name
18         self.role = role                                   # 实例变量
19         self.weapon = weapon
20
21         '''私有属性——只有在类Role中才能访问,外部无法访问'''
22         self.__life_value = life_value                 # 私有属性
23         self.money = money
24
25     def __del__(self):
26         '''
27         析构函数/方法,在程序运行完毕时或引用此类实例化的对象的引用次数为0时自动执行的操作
28         其实,写明析构函数只是对系统默认的析构方法的重载而已
29         '''
30         print('%s 彻底死了...' %self.name)
31
32     def show_status(self):
33         print('name:%s weapon:%s life_value:%s' %(self.name,
34                                                   self.weapon,
35                                                   self.__life_value))
36
37     def __shot(self):         # 私有方法
38         '''私有方法——只有在类Role中才能访问,外部无法访问'''
39         print('shooting...')
40
41     def got_shot(self):
42         self.__life_value -= 50   # 对私有属性的修改
43         print('%s:ah.....,I got a shot...' %self.name)
44
45     def buy_gun(self, gun_name):
46         print('%s just bought %s' %(self.name, gun_name))
47
48 r1 = Role('Tim', 'police', 'AK47')
49 r1.buy_gun('B22')
50 r1.got_shot()
51 # del r1            # 手动删除变量(取消引用关系,使得对象r1的引用次数为0)
52 # 此时程序尚未结束,会调用一次析构函数
53
54 r2 = Role('Tom', 'police', 'AK47')
55 r2.got_shot()
56 r2.show_status()
57 '''当没有主动删除引用关系时其结果是:(del r1)
58 Tim just bought B22
59 Tim:ah.....,I got a shot...
60 Tom:ah.....,I got a shot...
61 name:Tom weapon:AK47 life_value:50
62 Tim 彻底死了...
63 Tom 彻底死了...
64 '''
65
66 '''私有方法和私有属性外部无法访问'''
67 # print(r2.__life_value)
68 # AttributeError: 'Role' object has no attribute 'life_value'
69 # r2.__shot()
70 # AttributeError: 'Role' object has no attribute '__shot'

class code

说明并解释以上代码:

1、为什么需要__init__()构造方法?其作用是什么?为什么多个函数内部的形参的第一个参数默认是self,其作用是什么?

其解答参考: http://www.cnblogs.com/ant-colonies/p/6718388.html

2、经典类与新式类的比较:

参考:http://www.cnblogs.com/ant-colonies/p/6719724.html

3、类变量和实例变量:

参考:http://www.cnblogs.com/ant-colonies/p/6720885.html

4、私有属性和私有方法,析构函数

4、面向对象的特性

4.1 封装

封装是面向对象的特征之一,是对象和类概念的主要特性。

封装,也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。

4.2 继承

面向对象编程 (OOP) 语言的一个主要功能就是“继承”。继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。

通过继承创建的新类称为“子类”或“派生类”。

被继承的类称为“基类”、“父类”或“超类”。

继承的过程,就是从一般到特殊的过程。

要实现继承,可以通过“继承”(Inheritance)和“组合”(Composition)来实现。

在某些 OOP 语言中,一个子类可以继承多个基类。但是一般情况下,一个子类只能有一个基类,要实现多重继承,可以通过多级继承来实现。

继承概念的实现方式主要有2类:实现继承、接口继承。

  • 实现继承是指使用基类的属性和方法而无需额外编码的能力;
  • 接口继承是指仅使用属性和方法的名称、但是子类必须提供实现的能力(子类重构父类方法);
在考虑使用继承时,有一点需要注意,那就是两个类之间的关系应该是“属于”关系。例如,Employee 是一个人,Manager 也是一个人,因此这两个类都可以继承 Person 类。但是 Leg 类却不能继承 Person 类,因为腿并不是一个人。
 

抽象类仅定义将由子类创建的一般属性和方法。

OO开发范式大致为:划分对象→抽象类→将类组织成为层次化结构(继承和合成) →用类与实例进行设计和实现几个阶段。

单继承:即子类继承(唯一的)父类的属性和方法

 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 # Author: antcolonies
 4
 5 class People:
 6     def __init__(self, name, age):
 7         self.name = name
 8         self.age = age
 9
10     def eat(self):
11          print('%s is eatting...' %self.name)
12
13     def talk(self):
14          print('%s is talking...' %self.name)
15
16     def sleep(self):
17          print('%s is sleeping...' %self.name)
18
19
20 class Man(People):
21     def hell(self):            # 对父类的扩展
22         print('%s is going to hell...' %self.name)
23
24     def sleep(self):           # 重构父类
25         # People.sleep(self)
26         super(Man, self).sleep() # 先调用父类
27         print('man is sleeping')
28
29 class Woman(People):
30     def get_birth(self):
31         print('%s is building a baby...' %self.name)
32
33 m1 = Man('Tim', 22)
34 m1.eat()
35 m1.hell()
36 m1.sleep()
37
38 w1 = Woman('Kancy', 28)
39 w1.get_birth()
40 w1.eat()
41 w1.sleep()
42
43 '''
44 Tim is eatting...
45 Tim is going to hell...
46 Tim is sleeping...
47 man is sleeping
48 Kancy is building a baby...
49 Kancy is eatting...
50 Kancy is sleeping...
51 '''

单继承

 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 # Author: antcolonies
 4
 5 # class People: 经典类
 6 class People(object):    # 新式类
 7     def __init__(self, name, age):
 8         self.name = name
 9         self.age = age
10
11     def eat(self):
12          print('%s is eatting...' %self.name)
13
14     def talk(self):
15          print('%s is talking...' %self.name)
16
17     def sleep(self):
18          print('%s is sleeping...' %self.name)
19
20
21 class Man(People):
22     def __init__(self, name, age, position):
23         '''
24         重构构造方法,即子类的构造函数覆盖掉父类的,
25         当实例化一个Man类型的对象时,执行子类Man的构造函数
26         而不执行父类中的构造函数
27         '''
28         # super().__init__(name, age)
29         # People.__init__(self, name, age)    # 经典类写法
30         super(Man,self).__init__(name, age) # 新式类写法(这里重用了父类的构造方法)
31         self.position  = position
32         print('%s has a(n) %s' %(self.name, self.position))
33
34     def hell(self):
35         print('%s is going to hell...' %self.name)
36
37     def sleep(self):           # 重构父类
38         People.sleep(self)
39         print('man is sleeping')
40
41 class Relationship(object):
42     def make_friends(self, obj):
43         print('%s makes friends with %s' %(self.name, self.obj))
44
45 class Woman(People):
46     def get_birth(self):
47         print('%s is building a baby...' %self.name)
48
49 m1 = Man('Tim', 30, position='elite')
50 m1.eat()
51 m1.hell()
52 m1.sleep()
53
54 w1 = Woman('Kancy', 28)
55 w1.get_birth()
56
57 '''
58 Tim has a(n) elite
59 Tim is eatting...
60 Tim is going to hell...
61 Tim is sleeping...
62 man is sleeping
63 Kancy is building a baby...
64 '''
65
66 # m1 = Man('Tim', 30)  当实例化时,传入2个参数会抛异常
67 # 因为此时实例化执行的Man中的构造函数
68 '''
69 Traceback (most recent call last):
70   File "E:/python14_workspace/s14/day06/inherit_1.py", line 50, in <module>
71     m1 = Man('Tim', 30)
72 TypeError: __init__() missing 1 required positional argument: 'position'
73 '''

单继承之构造函数的重构

多(重)继承:是指python的类可以有两个以上父类,也即有类A,类B,类C,C同时继承类A与类B,此时C中可以使用A与B中的属性与方法

那么问题来了,如果A与B中具有相同名字的方法,这个时候python怎么调用的会是哪个方法呢?

代码段1:

 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 # Author: antcolonies
 4
 5 class A(object):
 6     def __init__(self):
 7         pass
 8     def foo(self):
 9         print('A foo...')
10
11 class B(object):
12     def __init__(self):
13         pass
14     def foo(self):
15         print('B foo...')
16
17 class C(A, B):
18     def __init__(self):
19         pass
20
21 testc = C()
22 testc.foo()        # A foo...

多继承_1

实际上打印出来的信息是 A foo,这就说明了调用的是A中的方法。其实在python2.2之后,多继承中基类的寻找顺序是一种广度优先算法,称之为C3的算法。而python2.2之前,使用的是深度优先算法来寻找基类方法。在类C的继承关系中,按照广度优先算法,则会先找到靠近C的基类A,在A中找到foo方法之后,就直接返回了,因此即使后面的基类B中也有foo方法,但是这里不会引用它。

 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 # Author: antcolonies
 4
 5 class A(object):
 6     def foo(self):
 7         print('A foo...')
 8
 9 class B(object):
10     def foo(self):
11         print('B foo...')
12     def bar(self):
13         print('B bar...')
14
15 class C1(A, B):
16     pass
17
18 class C2(A, B):
19     def bar(self):
20         print('C2-bar...')
21
22 class D(C1, C2):
23     pass
24
25 if __name__ == '__main__':
26     print(D.__mro__)
27     d = D()
28     d.foo()
29     d.bar()
30
31 # (<class '__main__.D'>,
32     # <class '__main__.C1'>,
33     # <class '__main__.C2'>,
34     # <class '__main__.A'>,
35     # <class '__main__.B'>,
36     # <class 'object'>)
37 # A foo...
38 # C2-bar...

多继承_2之__mro__属性

执行的结果为:

(<class '__main__.D'>, <class '__main__.C1'>, <class '__main__.C2'>, <class '__main__.A'>, <class '__main__.B'>, <type 'object'>)

A foo (实际上搜索顺序为D=>C1=>A)
C2 bar(实际上搜索顺序为D=>C1=>C2)

可以看到,foo找到的是A类中的方法,bar找到的是C2中的方法。

其实新式类的搜索方法是采用了“广度优先”的方式去查找属性。

只有新式类有__mro__属性,该属性标记了python继承层次中父类查找的顺序,python多重继承机制中就是按照__mro__的顺序进行查找,一旦找到对应属性,则查找马上返回。

经过上面的__mro__输出可以发现,D类的继承查找路径为:D=>C1=>C2=>A=>B=>object,通过该查找路径,foo方法将会调用A的foo方法,、bar方法将调用C2的方法,通过实际实验调用,查看输出内容确实与__mro__顺序一样。

参考了:http://www.cnblogs.com/panyinghua/p/3283726.html

多继承之super()

一、问题的发现与提出

  在Python类的方法(method)中,要调用父类的某个方法,在Python 2.2以前,通常的写法如代码段1:

代码段1:

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: antcoloniesclass A(object):def __init__(self):print('Enter A')print('Leave A')class B(A):def __init__(self):print('Enter B')A.__init__(self)print('Leave B')b = B()'''
Enter B
Enter A
Leave A
Leave B
'''

即,使用非绑定的类方法(用类名来引用的方法),并在参数列表中,引入待绑定的对象(self),从而达到调用父类的目的。

这样做的缺点是,当一个子类的父类发生变化时(如类B的父类由A变为C时),必须遍历整个类定义,把所有的通过非绑定的方法的类名全部替换过来,例如代码段2:

class B(C):    # A --> Cdef __init__(self):print "enter B"C.__init__(self) # A --> Cprint "leave B"

如果代码简单,这样的改动或许还可以接受。但如果代码量庞大,这样的修改可能是灾难性的。

因此,自Python 2.2开始,Python添加了一个关键字super,来解决这个问题。下面是Python 2.3的官方文档说明:

super(type[, object-or-type])
Return the superclass of type. If the second argument is omitted the super object, returned is unbound. If the second argument is an object, isinstance(obj, type) must be true. If the second argument is a type, issubclass(type2, type) must be true. super() only works for new-style classes.
A typical use for calling a cooperative superclass method is:
   class C(B):
       def meth(self, arg):
           super(C, self).meth(arg)

New in version 2.2.
从说明来看,可以把类B改写如代码段3:

class A(object):    # A must be new-style classdef __init__(self):print "enter A"print "leave A"class B(C):     # A --> Cdef __init__(self):print "enter B"super(B, self).__init__()print "leave B"

尝试执行上面同样的代码,结果一致,但修改的代码只有一处,把代码的维护量降到最低,是一个不错的用法。因此在我们的开发过程中,super关键字被大量使用,而且一直表现良好。

在我们的印象中,对于super(B, self).__init__()是这样理解的:super(B, self)首先找到B的父类(就是类A),然后把类B的对象self转换为类A的对象(通过某种方式,一直没有考究是什么方式,惭愧),然后“被转换”的类A对象调用自己的__init__函数。考虑到super中只有指明子类的机制,因此,在多继承的类定义中,通常我们保留使用类似代码段1的方法。

有一天某同事设计了一个相对复杂的类体系结构(我们先不要管这个类体系设计得是否合理,仅把这个例子作为一个题目来研究就好),代码如代码段4:

 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 # Author: antcolonies
 4
 5 class A(object):
 6     def __init__(self):
 7         print('Enter A')
 8         print('Leave A')
 9
10 class B(object):
11     def __init__(self):
12         print('Enter B')
13         print('Leave B')
14
15 class C(A):
16     def __init__(self):
17         print('Enter C')
18         super(C,self).__init__()
19         print('Leave C')
20
21 class D(A):
22     def __init__(self):
23         print('Enter D')
24         super(D,self).__init__()
25         print('Leave D')
26
27 class E(B,C):
28     def __init__(self):
29         print('Enter E')
30         B.__init__(self)
31         C.__init__(self)
32         print('Leave E')
33
34 class F(E,D):
35     def __init__(self):
36         print('Enter F')
37         E.__init__(self)
38         D.__init__(self)
39         print('Leave F')
40
41 if __name__ == '__main__':
42     print(F.__mro__)
43     f = F()
44
45 '''
46 (<class '__main__.F'>,
47  <class '__main__.E'>,
48  <class '__main__.B'>,
49  <class '__main__.C'>,
50  <class '__main__.D'>,
51  <class '__main__.A'>,
52  <class 'object'>)
53 Enter F
54 Enter E
55 Enter B
56 Leave B
57 Enter C
58 Enter D
59 Enter A
60 Leave A
61 Leave D
62 Leave C
63 Leave E
64 Enter D
65 Enter A
66 Leave A
67 Leave D
68 Leave F
69 '''
70 '''
71 object
72 |   \
73 B    A
74 |   / |
75 |  C  D
76 \  /  |
77   E   |
78    \  |
79      F
80 '''

View Code

明显地,类A和类D的初始化函数被重复调用了2次,这并不是我们所期望的结果!我们所期望的结果是最多只有类A的初始化函数被调用2次——其实这是多继承的类体系必须面对的问题。我们把代码段4的类体系画出来,如下图:
object
|   \
B    A
|   /  |
|  C  D
\  /   |
  E    |
   \   |
     F
按我们对super的理解,从图中可以看出,在调用类C的初始化函数时,应该是调用类A的初始化函数,但事实上却调用了类D的初始化函数。好一个诡异的问题!
也就是说,mro中记录了一个类的所有基类的类类型序列。查看mro的记录,发觉包含7个元素,7个类名分别为:
 F E B C D A object
从而说明了为什么在C.__init__中使用super(C, self).__init__()会调用类D的初始化函数了。 ???
我们把代码段4改写为:

 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 # Author: antcolonies
 4
 5 class A(object):
 6     def __init__(self):
 7         print('Enter A')
 8         super(A,self).__init__()     # New
 9         print('Leave A')
10
11 class B(object):
12     def __init__(self):
13         print('Enter B')
14         super(B, self).__init__()  # New
15         print('Leave B')
16
17 class C(A):
18     def __init__(self):
19         print('Enter C')
20         super(C,self).__init__()
21         print('Leave C')
22
23 class D(A):
24     def __init__(self):
25         print('Enter D')
26         super(D,self).__init__()
27         print('Leave D')
28
29 class E(B,C):
30     def __init__(self):
31         print('Enter E')
32         super(E, self).__init__()  # change
33         print('Leave E')
34
35 class F(E,D):
36     def __init__(self):
37         print('Enter F')
38         super(E, self).__init__()  # change
39         print('Leave F')
40
41 if __name__ == '__main__':
42     print(F.__mro__)
43     f = F()
44
45 '''
46 (<class '__main__.F'>,
47  <class '__main__.E'>,
48  <class '__main__.B'>,
49  <class '__main__.C'>,
50  <class '__main__.D'>,
51  <class '__main__.A'>,
52  <class 'object'>)
53 Enter F
54 Enter E
55 Enter B
56 Enter C
57 Enter D
58 Enter A
59 Leave A
60 Leave D
61 Leave C
62 Leave B
63 Leave E
64 Leave F
65 '''
66 '''
67 object
68 |   \
69 B    A
70 |   / |
71 |  C  D
72 \  /  |
73   E   |
74    \  |
75      F
76 '''

View Code

明显地,F的初始化不仅完成了所有的父类的调用,而且保证了每一个父类的初始化函数只调用一次。
再看类结构:

    object/   \/      A|     /   \B-1   C-2  D-2\   /    /E-1   /\  /F

E-1,D-2是F的父类,其中表示E类在前,即F(E,D)。
所以初始化顺序可以从类结构图来看出 : F->E->B -->C --> D --> A
由于C,D有同一个父类,因此会先初始化D再是A。

二、延续的讨论
我们再重新看上面的类体系图,如果把每一个类看作图的一个节点,每一个从子类到父类的直接继承关系看作一条有向边,那么该体系图将变为一个有向图。不难发现mro的顺序正好是该有向图的一个拓扑排序序列。

从而,我们得到了另一个结果——Python是如何去处理多继承。支持多继承的传统的面向对象程序语言(如C++)是通过虚拟继承的方式去实现多继承中父类的构造函数被多次调用的问题,而Python则通过mro的方式去处理。

但这给我们一个难题:对于提供类体系的编写者来说,他不知道使用者会怎么使用他的类体系,也就是说,不正确的后续类,可能会导致原有类体系的错误,而且这样的错误非常隐蔽的,也难于发现。

三、小结
  1. super并不是一个函数,是一个类名,形如super(B, self)事实上调用了super类的初始化函数,产生了一个super对象;
  2. super类的初始化函数并没有做什么特殊的操作,只是简单记录了类类型和具体实例;
  3. super(B, self).func的调用并不是用于调用当前类的父类的func函数;
  4. Python的多继承类是通过mro的方式来保证各个父类的函数被逐一调用,而且保证每个父类函数只调用一次(如果每个类都使用super);
  5. 混用super类和非绑定的函数是一个危险行为,这可能导致应该调用的父类函数没有调用或者一个父类函数被调用多次。

参考了:http://www.cnblogs.com/lovemo1314/archive/2011/05/03/2035005.html

多重继承实例:

  1 #!/usr/bin/env python
  2 # -*- coding:utf-8 -*-
  3 # Author: antcolonies
  4
  5 class School(object):
  6     def __init__(self, name, addr):
  7         self.name = name
  8         self.addr = addr
  9         self.students = []
 10         self.teachers = []
 11         self.staffs = []
 12     def enroll(self, stu_obj):
 13         print('enrolling for students...')
 14         self.students.append(stu_obj)
 15     def hire(self, staff_obj):
 16         print('hiring new staffs...')
 17         self.staffs.append(staff_obj)
 18
 19
 20 class SchoolMember(object):
 21     def __init__(self, name, age, gender):
 22         self.name = name
 23         self.age = age
 24         self.gender = gender
 25
 26     def tell(self):
 27         # print()
 28         pass
 29
 30 class Teacher(SchoolMember):
 31     def __init__(self, name, age, gender, salary, course):
 32         super(Teacher, self).__init__(name, age, gender)
 33         self.salary = salary
 34         self.course = course
 35
 36     def tell(self):
 37         print('''
 38          ----- info of Teacher:%s ----
 39          Name:    %s
 40          Gender:  %s
 41          Salary:  %s
 42          Course:  %s
 43         ''' %(self.name, self.name, self.gender, self.salary, self.course))
 44
 45     def teach(self):
 46         print('%s is teaching course [%s]' %(self.name, self.course))
 47
 48
 49 class Student(SchoolMember):
 50     def __init__(self, name, age, gender, std_id, grade):
 51         super(Student, self).__init__(name, age, gender)
 52         self.std_id = std_id
 53         self.grade = grade
 54     def tell(self):
 55         print('''
 56          ----- info of Student:%s -----
 57          Name:    %s
 58          Gender:  %s
 59          Std_is:  %s
 60          Grade:   %s
 61         ''' %(self.name, self.name, self.gender, self.std_id, self.grade))
 62     def pay_tuition(self, amount):
 63         print('%s has paid tuition for $%s' %(self.name, amount))
 64
 65
 66 school = School('oldboyIT', 'sandriver')
 67
 68 t1 = Teacher('Oldboy', 56, 'MF', 2000000, 'Linux')
 69 t2 = Teacher('Alex', 22, 'MF', 3000000, 'PythonDevOps')
 70
 71 s1 = Student('Tim', 36, 'MF', 1001, 'PythonDevOps')
 72 s2 = Student('Bruce', 19, 'MF', 1002, 'Linux')
 73
 74 t1.tell()
 75 s1.tell()
 76
 77 school.enroll(s1)
 78 school.enroll(s2)
 79 school.hire(t1)
 80
 81 print(school.students)
 82 print(school.staffs)
 83 school.staffs[0].teach()
 84
 85 for stu in school.students:
 86     stu.pay_tuition(5000)
 87
 88 '''
 89          ----- info of Teacher:Oldboy ----
 90          Name:    Oldboy
 91          Gender:  MF
 92          Salary:  2000000
 93          Course:  Linux
 94
 95
 96          ----- info of Student:Tim -----
 97          Name:    Tim
 98          Gender:  MF
 99          Std_is:  1001
100          Grade:   PythonDevOps
101
102 enrolling for students...
103 enrolling for students...
104 hiring new staffs...
105 [<__main__.Student object at 0x0000000001F448D0>, <__main__.Student object at 0x0000000001F44908>]
106 [<__main__.Teacher object at 0x0000000001F44860>]
107 Oldboy is teaching course [Linux]
108 Tim has paid tuition for $5000
109 Bruce has paid tuition for $5000
110 '''    

multinherit

4.3 多态

多态(Polymorphism),是指面向对象程序运行时,相同的消息可能会送给多个不同的类之对象,而系统可依据对象所属类,引发对应类的方法,而有不同的行为。
简单来说,所谓多态意指相同的消息给予不同的对象会引发不同的动作称之。
在面向对象程序设计中,多态一般指子类型多态(Subtype polymorphism)。
多态性——允许你将父对象设置成为和一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。简单的说,就是一句话:允许将子类类型的指针赋值给父类类型的指针。
那么,多态的作用是什么呢?我们知道,封装可以隐藏实现细节,使得代码模块化;继承可以扩展已存在的代码模块(类);它们的目的都是为了——代码重用。而多态则是为了实现另一个目的——接口重用!多态的作用,就是为了类在继承和派生的时候,保证使用“家谱”中任一类的实例的某一属性时的正确调用。
Pyhon 很多语法都是支持多态的,比如 len(),sorted(), 你给len传字符串就返回字符串的长度,传列表就返回列表长度。

多态实例1(比如不同类型的动物的叫声):

 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 # Author: antcolonies
 4
 5 class Animal(object):
 6     def __init__(self, name):
 7         self.name = name
 8     def talk(self):
 9         raise NotImplementedError('Subclass must implement abstract method')
10
11 class Cat(Animal):
12     def talk(self):
13         return 'Meow...'
14
15 class Dog(Animal):
16     def talk(self):
17         return 'Woof...'
18
19 animals = [Cat('Missy'),
20            Cat('Jeff'),
21            Dog('Lassie')]
22
23 for animal in animals:
24     print('%s : %s' %(animal.name, animal.talk()))
25
26 '''
27 Missy : Meow...
28 Jeff : Meow...
29 Lassie : Woof...
30 '''

polymorphism

 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 # Author: antcolonies
 4
 5 class Animal(object):
 6     def __init__(self, name):
 7         self.name = name
 8     def talk(self):
 9         raise NotImplementedError('Subclass must implement abstract method')
10
11     @staticmethod           # 静态方法
12     def animal_talk(obj):
13         return obj.talk()
14
15 class Cat(Animal):
16     def talk(self):
17         return 'Meow...'
18
19 class Dog(Animal):
20     def talk(self):
21         return 'Woof...'
22
23 c = Cat('Missy')
24 c1 = Cat('Jeff')
25 d = Dog('Lassie')
26
27 print('%s : %s' %(c.name, Animal.animal_talk(c)))
28 print('%s : %s' %(c1.name, Animal.animal_talk(c1)))
29 print('%s : %s' %(d.name, Animal.animal_talk(d)))
30
31 '''
32 Missy : Meow...
33 Jeff : Meow...
34 Lassie : Woof...
35 '''

polymorphism_staticmethod

多态实例2:一个简单的日志记录函数,用判断实现的,重构为面向对象多态来实现。(如果有大量的判断语句,就可以用多态来实现)

 1 # 工程应用
 2 # 一个简单的日志记录函数,用判断实现的,重构为面向对象多态来实现。
 3 #如果有大量的判断语句,就可以用多态来实现。
 4
 5
 6 def log_msg(log_type):
 7     msg = 'Operation successful'
 8     if log_type == 'file':
 9         log_file.write(msg)
10     elif log_type == 'database':
11         cursor.execute('INSERT INTO log_table (MSG) VALUES ('?')', msg)
12
13 #重构
14
15
16 class FileLogger(object):
17
18     def log(self, msg):
19         log_file.write(msg)
20
21
22 class DbLogger(object):
23
24     def log(self, msg):
25         cursor.execute('INSERT INTO log_table (MSG) VALUES ('?')', msg)
26
27
28 def log_msg(obj):
29     msg = 'Operation successful'
30     obj.log(msg)

不同类型的日志处理

参考了: https://www.oschina.net/code/snippet_1448389_49611

转载于:https://www.cnblogs.com/ant-colonies/p/6716703.html

day06 - Python - 面向对象相关推荐

  1. python面向对象的优点_Python面向对象编程——总结面向对象的优点

    Python面向对象编程--总结面向对象的优点 一.从代码级别看面向对象 1.在没有学习类这个概念时,数据与功能是分离的 def exc1(host,port,db,charset): conn=co ...

  2. 这可能是Python面向对象编程的最佳实践

    作者 | 崔庆才 来源 | 进击的Coder(ID:FightingCoder) Python 是支持面向对象的,很多情况下使用面向对象编程会使得代码更加容易扩展,并且可维护性更高,但是如果你写的多了 ...

  3. 第八课.Python面向对象(二)

    类的继承和多态 继承和多态与函数有共同的目的,都是为了减少代码的冗余,提高复用的效率: 根据"Python面向对象(一)"的笔记,我现在定义一个Cinema类: #父类 class ...

  4. Python面向对象编程:类继承和其衍生术语

    Python面向对象编程03:类继承和其衍生术语 前面我们讲到过正则表达式字符等,上一篇分享了面向对象编程和类的结构,最后稍微提到了继承. Python面向对象编程:深度认识类class_ Pytho ...

  5. 《Python面向对象编程指南》——1.2 基类中的__init__()方法

    本节书摘来自异步社区<Python面向对象编程指南>一书中的第1章,第1.2节,作者[美]Steven F. Lott, 张心韬 兰亮 译,更多章节内容可以访问云栖社区"异步社区 ...

  6. Python 面向对象 基础

    编程范式概述: 面向过程 和 面向对象 以及函数式编程 面向过程:(Procedure Oriented)是一种以事件为中心的编程思想. 就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实 ...

  7. python面向对象类属性_python面向对象之类属性和类方法案例分析

    本文实例讲述了python面向对象之类属性和类方法.分享给大家供大家参考,具体如下: 目标 类的结构 类属性和实例属性 类方法和静态方法 01. 类的结构 1.1 术语 -- 实例 使用面相对象开发, ...

  8. 关于python面向对象编程中、下列说法中_关于Python面向对象编程的知识点总结

    前言 如果你以前没有接触过面向对象的编程语言,那你可能需要先了解一些面向对象语言的一些基本特征,在头脑里头形成一个基本的面向对象的概念,这样有助于你更容易的学习Python的面向对象编程. 接下来我们 ...

  9. python对象编程例子-这是我见过最详细的Python面向对象编程!建议收藏!

    原标题:这是我见过最详细的Python面向对象编程!建议收藏! 面向对象编程和函数式编程(面向过程编程)都是程序设计的方法,不过稍有区别. 面向过程编程: 1. 导入各种外部库 2. 设计各种全局变量 ...

最新文章

  1. Python divmod() 函数
  2. SQL与NoSQL区别-查询方式
  3. 开发日记-20190405
  4. Python中类的定义与使用
  5. java默认xmx修改,Java 8中的默认Xmxsize
  6. 【机器学习】FaceBook开源全网第一个时序王器--Kats。
  7. 现有工程项目上加响应式
  8. 【GIS风暴】GIS拓扑关系原理详解
  9. php for next,Nextcloud停留无限登录页面 PHP7的问题及解决方案
  10. JNI_Android项目中调用.so动态库实现详解【转】
  11. sqlerver 字符串转整型_Sqlerver进行模糊查询like和转义字符
  12. Hadoop学习整理
  13. Java代码实现时钟
  14. 常用的3款光学仿真软件分析---来源网络
  15. input 输入法禁用
  16. elasticsearch 在不是 not_analyzed 的前提下如何全匹配的效果
  17. Mac升级node版本
  18. 最热网友收藏:深入理解C语言指针的奥秘((2007年第3周)
  19. 用指向指针的指针方法对n个整数排序输出
  20. 张量分解学习(一 基础铺垫)

热门文章

  1. HOG图像特征提取算法
  2. 【笑话】传说中的八级英语。。。怕了吧?(应该真实)
  3. 从Apple最新的OS Yosemite的LaunchPad中删除旧的iMovie
  4. matlab中的取余函数
  5. 关于matlab的mod问题
  6. 前端技术搭建五子棋游戏(内含源码)
  7. 嵌入式Linux Qt设备随机死机问题分析记录
  8. 肱动脉穿刺技术并发症,预防,护理
  9. 职场新人完全转型七大要素
  10. 2021-03-12-C++学习之10-类和对象-多态