多继承和super()

2.7:
经典类,深度优先 class foo()
新式类:广度优先 class foo(object)
3.X:
均是广度优先
python类的体系中如果出现了继承,子类如果没有写自己的初始化方法,会自动继承第一个父类的方法,所以我们需要自己写子类的初始化:
如果像下面的代码:

class A:
def __init__(self):

print("Enter A")
print("Leave A")


class B(A):
def __init__(self):

print("Enter B")
A.__init__(self)
print("Leave B")


class C(A):
def __init__(self):

print("Enter C")
A.__init__(self)
print("Leave C")


class D(A):
def __init__(self):

print("Enter D")
A.__init__(self)
print("Leave D")


class E(B, C, D):
def __init__(self):

print("Enter E")
B.__init__(self)
C.__init__(self)
D.__init__(self)
print("Leave E")
E()

'''会造成公共父类A被调用多次。
正确的办法是使用super,它会把当前的类返回一个父类的对象,注意你在体系中使用了super那么所有的地方都要使用super,
否则可能造成父类多次被调用或者不能调用所有的父类方法(这是由super的机制造成的,具体实现不懂),
super保证所有父类只会执行一次,而且修改代码时也很方便:'''

class A(object):
def __init__(self):
print('Enter A')
self.__test = 0


class C(object):
def __init__(self):
print('Enter C')
super().__init__()
self.__test2 = 3


class D(object):
def __init__(self):
super().__init__()
print('enter D')


class B(A):
def __init__(self):
print('Enter B')
super().__init__()

def set(self):
self.__test = 2

def display(self):
print(dir(self))


class E(C, D):
def __init__(self):
print('Enter E')
super().__init__();

#python中使用多继承,会涉及到查找顺序(MRO)、重复调用(钻石继承,也叫菱形继承问题)等

#MRO

#MRO即method resolution order,用于判断子类调用的属性来自于哪个父类。
# 在Python2.3之前,MRO是基于深度优先算法的,自2.3开始使用C3算法,定义类时需要继承object,这样的类称为新式类,否则为旧式类
#旧式类查找属性时是深度优先搜索,新式类则是广度优先搜索

#在C++和Java中,使用继承时,子类的构造函数会自动调用父类的构造函数,
# 但在Python中,子类必须显式的在__init__()函数中再次调用父类中的__init__()函数。

########################
# 例2:
# python 普通继承方式和super继承方式
# Python中对象方法第一个参数一般都命名为self(相当于其它语言的this),用于传递对象本身,而在调用的时候则不必显式传递,系统会自动传递。

# 举一个很常见的例子:

class Foo:
def bar(self, message):
print(message)
Foo().bar("Hello, World.")


# 当存在继承关系的时候,有时需要在子类中调用父类的方法,此时最简单的方法是把对象调用转换成类调用,需要注意的是这时self参数需要显式传递,例如:

class FooParent:
def bar(self, message):
print(message)

class FooChild(FooParent):
def bar(self, message):
FooParent.bar(self, message)


# 这样做有一些缺点,比如说如果修改了父类名称,那么在子类中会涉及多处修改,另外,Python是允许多继承的语言,
# 如上所示的方法在多继承时就需要重复写多次,显得累赘。为了解决这些问题,Python引入了super()机制,例子代码如下:
# 语法格式:supper(自己的类名,self)

class FooParent:
def bar(self, message):
print(message)


class FooChild(FooParent):
def bar(self, message):
super(FooChild, self).bar(message)


# 表面上看 super(FooChild, self).bar(message)方法和FooParent.bar(self, message)方法的结果是一致的,实际上这两种方法的内部处理机制大大不同,当涉及多继承情况时,就会表现出明显的差异来,直接给例子:

# 代码一:

class A:
def __init__(self):
print("Enter A")
print("Leave A")

class B(A):
def __init__(self):
print("Enter B")
A.__init__(self)
print("Leave B")

class C(A):
def __init__(self):
print("Enter C")
A.__init__(self)
print("Leave C")

class D(A):
def __init__(self):
print("Enter D")
A.__init__(self)
print("Leave D")

class E(B, C, D):
def __init__(self):
print("Enter E")
B.__init__(self)
C.__init__(self)
D.__init__(self)
print("Leave E")

E()

# 结果:
#
# Enter E
# Enter B
# Enter A
# Leave A
# Leave B
# Enter C
# Enter A
# Leave A
# Leave C
# Enter D
# Enter A
# Leave A
# Leave D
# Leave E

# 执行顺序很好理解,唯一需要注意的是公共父类A被执行了多次。

# 代码二:

class A:
def __init__(self):
print("Enter A")
print("Leave A")

class B(A):
def __init__(self): #相当于覆盖了父类的构造函数
print("Enter B") #定义属于自己的某些属性
super(B, self).__init__() #super内填写自己的类名。本句相当于继承父类的构造函数
print("Leave B") #定义属于自己的某些属性

print("Leave B")

class C(A):
def __init__(self):
print("Enter C")
super(C, self).__init__() #super内填写自己的类名
print("Leave C")

class D(A):
def __init__(self):
print("Enter D")
super(D, self).__init__() #super内填写自己的类名
print("Leave D")

class E(B, C, D):
def __init__(self):
print("Enter E")
super(E, self).__init__() #super内填写自己的类名
print("Leave E")

E()

# 结果:

# Enter E
# Enter B
# Enter C
# Enter D
# Enter A
# Leave A
# Leave D
# Leave C
# Leave B
# Leave E

#执行E——》执行父类B的__init__ ——》执行父类C的__init__ ——》执行父类D的__init__ ——》执行爷类A的__init__,【B、C、D有三个super(),只执行一个】
# ——》执行D的super()后面的命令 ——》执行C的super()后面的命令 ——》执行B的super()后面的命令 ——》执行E的super()后面的命令
# 在super机制里可以保证公共父类仅被执行一次,至于执行的顺序,是按照mro进行的(E.__mro__)。
#在设计类的时候,我们优先考虑通过继承多个类来组合多个类的功能,而不是设计多层次的复杂的继承关系。

########################################
# __slots__的使用
# 作用:限制实例属性只能为指定的属性
# 要注意,__slots__定义的属性仅对当前类实例起作用,对继承的子类是不起作用的。

class Student(object):
__slots__ = ('name','age')

s = Student()
s.age = 12
s.name = 'jack'
s.sex = 'none' #sex没有被放到__slots__中,所以不能绑定sex属性,试图绑定sex将得到AttributeError的错误。


posted @ 2017-10-25 10:12  浆糊jun  阅读(591)  评论(0)    收藏  举报