关于Python的super用法以及mro机制

来源:互联网 发布:复古相机软件 编辑:程序博客网 时间:2024/06/05 07:37

本文转载自http://www.cnblogs.com/dkblog/archive/2011/02/24/1980654.html,因为原文作者写的不错,就做一回大自然的搬运工,只做自己学习之用。侵删。

Python中对象方法的定义很怪异,第一个参数一般都命名为self(相当于其它语言的this),用于传递对象本身,而在调用的时候则不必显式传递,系统会自动传递。

举一个很常见的例子:

>>> class Foo:
def bar(self, message):
print(message)

>>> Foo().bar("Hello, World.")
Hello, World.

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

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

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

>>> FooChild().bar("Hello, World.")
Hello, World.

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

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


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


>>> FooChild().bar("Hello, World.")
Hello, World.

表面上看 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__()
print("Leave B")

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

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

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

E()

结果:

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

在super机制里可以保证公共父类仅被执行一次,至于执行的顺序,是按照mro进行的(E.__mro__)。

所谓mro机制

转自http://blog.csdn.net/imzoer/article/details/8737642

还记得什么是新式类和旧式类吗?

Python中,一个class继承于object,或其bases class里面任意一个继承于object,这个class都是new-style class。

-----------------------------------------------

在python中,类是可以多重继承的。python类中的所有成员变量都是类似Java语言中的public的。

------------------------------类属性-----------------------------

在python中,类中定义的函数也是对象。也可以修改赋值。

[python] view plain copy
  1. # -*- coding:utf-8 -*-  
  2. class A(object):  
  3.     def f(self):  
  4.         print "f"  
  5. def ff():  
  6.     print "ff"  
  7. a=A()  
  8. a.f()  
  9. xf=a.f  
  10. xf()  
  11. a.f=ff  
  12. a.f()  

这个例子的输出结果如下:

f

ff

ff

通过上面的结果,可看出来,a.f=ff的时候,对象a中的函数已经被修改成ff函数了。

另外,xf也是一个函数对象。

------------------------------------------------

对于继承,python提供了两个函数:issubclass()和isinstance()

看例子:

>>> issubclass(bool,int)
True
>>>

结果输出是True,说明bool是int的子类。

[python] view plain copy
  1. # -*- coding:utf-8 -*-  
  2. class A(object):  
  3.     def a(self):  
  4.         print "a"  
  5.   
  6. if __name__ == "__main__":  
  7.     a=A()  
  8.     print isinstance(a,A)  

主要还是学习一下多重继承的概念。

py中多重继承的语法如下:

class Myclass(base1,base2,base3):

---------------------------------------------------

mro即method resolution order主要用于在多继承时判断调的属性的路径(来自于哪个类)。
之前查看了很多资料,说mro是基于深度优先搜索算法的。但不完全正确在Python2.3之前是基于此算法,但从Python2.3起应用了新算法:C3算法。
为什么采用C3算法
C3算法最早被提出是用于Lisp的,应用在Python中是为了解决原来基于深度优先搜索算法不满足本地优先级,和单调性的问题。
本地优先级:指声明时父类的顺序,比如C(A,B),如果访问C类对象属性时,应该根据声明顺序,优先查找A类,然后再查找B类。
单调性:如果在C的解析顺序中,A排在B的前面,那么在C的所有子类里,也必须满足这个顺序。
------------------------------新式类和旧式类中查找属性的顺序不同-------------------------------------
在新式类中,查找一个要调用的函数或者属性的时候,是广度优先搜搜的。
在旧式类当中,是深度优先搜索的。如下图所示:
看下面的例子:
[python] view plain copy
  1. # -*- coding:utf-8 -*-  
  2.   
  3. class D(object):  
  4.     def foo(self):  
  5.         print "class D"  
  6.   
  7. class B(D):  
  8.     pass  
  9.   
  10. class C(D):  
  11.     def foo(self):  
  12.         print "class C"  
  13.   
  14. class A(B, C):  
  15.     pass  
  16.   
  17. f = A()  
  18. f.foo()  

例子中定义D类的时候,D是新式类,所以D的所有子类都是新式类。
A的实例对象f在调用foo函数的时候,根据广度优先搜索原则,调用的是C类里面的foo函数。
上面的代码输出class C
如果定义D类的时候直接class D,而不是class D(object),那么上述代码就该输出class D了。

---------------------------------------

命名空间

python中,不同命名空间中的内容可以重名。比如说在A模块中定义一个max函数,在B模块中也定义一个max函数,那么二者是不冲突的。在调用的时候,只需要在函数名字前面加上模块名字即可。

在python中,一切都是对象。严格的说,在模块中,对名字的引用就是属性引用。在表达式modulename.functionname中,modulename是一个模块对象。function那么则是该对象的一个属性。

属性分为只读的和可写的。如果是可写的属性,那么就可以使用del来删除了。比如说在一个类中删除一个属性的例子如下:

[python] view plain copy
  1. # -*- coding:utf-8 -*-  
  2.   
  3. class OOO(object):  
  4.     def __init__(self, value):  
  5.         self.value=value  
  6.       
  7. if __name__ == "__main__":  
  8.     a=OOO(100)  
  9.     print a.value  
  10.     del a.value  
  11.     print a.value  

执行代码的结果:

Traceback (most recent call last):
  File "C:\Users\naughty\workspace\ttt\com\d.py", line 15, in <module>
100
    print a.value
AttributeError: 'OOO' object has no attribute 'value'

可以看到,属性value在删除之前是可以输出的。删除之后,再次输出就会抛出异常了。

也可以删除引入的另外一个模块的内容:

[python] view plain copy
  1. # -*- coding:utf-8 -*-  
  2.   
  3. import data  
  4.   
  5. if __name__ == "__main__":  
  6.     print data.a  
  7.     print data.b  
  8.     del data.a  
  9.     print data.a  

data模块如下:

[python] view plain copy
  1. # -*- coding:utf-8 -*-  
  2.   
  3. a = "aa"  
  4. b = "bb"  

---------------------------------------------

既然涉及到了命名空间,那么有必要说一下global的使用。

global的使用了是为了在一个代码块中声明一个变量是全局变量。

[python] view plain copy
  1. # -*- coding:utf-8 -*-  
  2.   
  3. import data  
  4.   
  5. a="global a!"  
  6.   
  7. def modify():  
  8.     global a  
  9.     a="inner a!"  
  10.     print a  
  11.   
  12.   
  13. if __name__ == "__main__":  
  14.     print a  
  15.     modify()  
  16.     print a   

在上面这个例子中,modify函数中使用了global,然后修改了a的值,并打印。

在代码最后也打印了a的值。

执行代码输出如下:

global a!
inner a!
inner a!#这里的值被修改了

这说明,global确实起到作用了。

如果这里不使用global的话,那么根据python对变量赋值的原则,这里会在modify这个函数的局部空间中修改变量a,并不会反映到全局。

删除global a之后,再次执行,输出如下:

global a!
inner a!
global a!


0 0