http://blog.csdn.net/pipisorry/article/details/46381341
python面向对象基础知识
面向对象的三大特性:封装、继承和多态。
继承
在Python中,同时支持单继承与多继承,一般语法如下:
class SubClassName(ParentClass1 [, ParentClass2, ...]):
class_suite
继承示例1
class Parent(object):
x = 1
class Child1(Parent):
pass
class Child2(Parent):
pass
print Parent.x, Child1.x, Child2.x
Child1.x = 2
print Parent.x, Child1.x, Child2.x
Parent.x = 3
print Parent.x, Child1.x, Child2.x
以上代码的输出是:
1 1 1
1 2 1
3 2 3
最后一行的输出是 3 2 3 而不是 3 2 1。为什么改变了 Parent.x 的值还会改变 Child2.x 的值,但是同时 Child1.x 值却没有改变?这个答案的关键是,在 Python 中,类变量在内部是作为字典处理的。如果一个变量的名字没有在当前类的字典中发现,将搜索祖先类(比如父类)直到被引用的变量名被找到(如果这个被引用的变量名既没有在自己所在的类又没有在祖先类中找到,会引发一个 AttributeError 异常 )。
继承示例2
如果继承一个函数,而想重写,但是重写的函数中想要引用不重写时函数的值,可以这样
def get_params(self, deep=True):
params = super(DNN, self).get_params()
内建函数insubclass()
实现继承之后,子类将继承父类的属性,也可以使用内建函数insubclass()来判断一个类是不是另一个类的子孙类:
Python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | classParent(object): ''' parent class ''' numList=[] defnumAdd(self,a,b): returna+b
classChild(Parent): pass
c=Child() # subclass will inherit attributes from parent class Child.numList.extend(range(10)) printChild.numList print"2 + 5 =",c.numAdd(2,5)
# built-in function issubclass() print issubclass(Child,Parent) print issubclass(Child,object)
# __bases__ can show all the parent classes printChild.__bases__
# doc string will not be inherited printParent.__doc__ printChild.__doc__ |
文档字符串__doc__属性
文档字符串对于类,函数/方法,以及模块来说是唯一的,也就是说__doc__属性是不能从父类中继承来的。
继承中的__init__
1. 如果子类没有定义自己的初始化函数,父类的初始化函数会被默认调用;但是如果要实例化子类的对象,则只能传入父类的初始化函数对应的参数,否则会出错。
Python
1 2 3 4 5 6 7 8 9 10 11 12 | classParent(object): def__init__(self,data): self.data=data print"create an instance of:",self.__class__.__name__ print"data attribute is:",self.data
classChild(Parent): pass
c=Child("init Child") c=Child() #出错 |
2. 如果子类定义了自己的初始化函数,而没有显示调用父类的初始化函数,则父类的属性不会被初始化
Python
1 2 3 4 5 6 7 8 9 10 11 12 | classParent(object): def__init__(self,data): self.data=data print"create an instance of:",self.__class__.__name__ print"data attribute is:",self.data
classChild(Parent): def__init__(self): print"call __init__ from Child class"
c=Child() print c.data |
3. 如果子类定义了自己的初始化函数,显示调用父类,子类和父类的属性都会被初始化
Python
1 2 3 4 5 6 7 8 9 10 11 12 13 | classParent(object): def__init__(self,data): self.data=data print"create an instance of:",self.__class__.__name__ print"data attribute is:",self.data
classChild(Parent): def__init__(self): print"call __init__ from Child class" super(Child,self).__init__("data from Child")
c=Child() printc.data |
代码的输出为:
super
通过super来调用父类__init__方法,下面看看super的使用。
在子类中,一般会定义与父类相同的属性(数据属性,方法),从而来实现子类特有的行为。也就是说,子类会继承父类的所有的属性和方法,子类也可以覆盖父类同名的属性和方法。
有时候可能需要在子类中访问父类的一些属性:
Python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | class Parent(object): fooValue = "Hi, Parent foo value" def foo(self): print "This is foo from Parent"
class Child(Parent): fooValue = "Hi, Child foo value" def foo(self): print "This is foo from Child" print Parent.fooValue # use Parent class name and self as an argument Parent.foo(self)
c = Child() c.foo() |
这时候,可以通过父类名直接访问父类的属性,当调用父类的方法是,需要将”self”显示的传递进去的方式。
这种方式有一个不好的地方就是,需要经父类名硬编码到子类中,为了解决这个问题,可以使用Python中的super关键字:
Python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | classParent(object): fooValue="Hi, Parent foo value" deffoo(self): print"This is foo from Parent"
classChild(Parent): fooValue="Hi, Child foo value" deffoo(self): print"This is foo from Child" # use super to access Parent attribute print super(Child,self).fooValue super(Child,self).foo()
c=Child() c.foo() |
对于”super(Child, self).foo()”可以理解为,首先找到Child的父类Parent,然后调用父类的foo方法,同时将Child的实例self传递给foo方法。
但是,如果当一个子类有多个父类的时候,super会如何工作呢?这是就需要看看MRO的概念了。
多类继承中的方法解析顺序MRO(Method Resolution Order)
Python2.3之前,使用的是老式继承,直接看例子:
>>> O = object >>> class X(O): pass >>> class Y(O): pass >>> class A(X,Y): pass >>> class B(Y,X): pass
这样下来,方法查找链就是这样的:
----------- | | | O | | / \ | - X Y / | / | / | / |/ A B \ / ?
因此,不能再有一个新的类来继承 A 和 B,因为 A 的继承顺序是 X-Y,而 B 的继承顺序是 Y-X,那么到底是先在 X 里查找还是先在 Y 里查找呢?
为了解决这个问题,引入了 C3 MRO,还是以例子来说明:
>>> O = object >>> class F(O): pass >>> class E(O): pass >>> class D(O): pass >>> class C(D,F): pass >>> class B(D,E): pass >>> class A(B,C): pass
那么方法查找链是这样:
6 --- Level 3 | O | (more general) / --- \ / | \ | / | \ | / | \ | --- --- --- | Level 2 3 | D | 4| E | | F | 5 | --- --- --- | \ \ _ / | | \ / \ _ | | \ / \ | | --- --- | Level 1 1 | B | | C | 2 | --- --- | \ / | \ / \ / --- Level 0 0 | A | (more specialized) ---
计算的时候就是:
L[O] = O L[D] = D O L[E] = E O L[F] = F O L[B] = B + merge(DO, EO, DE)
规则就是,以继承时的声明为顺序,每次取方法查找链的头一个,如果这个头不在后面的方法查找链的尾部,那么就把他放到方法查找链 里,首先方法查找肯定是在 B
里进行,然后是 merge(DO, EO, DE)
,D
是一个好的节点,因为 D
不在 DO, EO, DE
的尾部。然后是 O,O
在 EO
的尾部。然后是 E
,然后是 O
。
所以最后方法查找链就是 B -> D -> E -> O
。
同样,拿上面的例子来看,C3 MRO的查找顺序就应该是 A -> X -> Y -> B -> O
假设现在有一个如下的继承结构,首先通过类名显示调用的方式来调用父类的初始化函数:
Python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | class 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"
class C(A): def __init__(self): print " --->Enter C" A.__init__(self) print " <---Leave C"
class D(B, C): def __init__(self): print "---->Enter D" B.__init__(self) C.__init__(self) print "<----Leave D"
d = D() |
从输出中可以看到,类A的初始化函数被调用了两次,这不是我们想要的结果:
下面,我们通过super方式来调用父类的初始化函数:
Python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | classA(object): def__init__(self): print" ->Enter A" print" <-Leave A"
classB(A): def__init__(self): print" -->Enter B" super(B,self).__init__() print" <--Leave B"
classC(A): def__init__(self): print" --->Enter C" super(C,self).__init__() print" <---Leave C"
classD(B,C): def__init__(self): print"---->Enter D" super(D,self).__init__() print"<----Leave D"
d=D() |
通过输出可以看到,当使用super后,A的初始化函数只能调用了一次:
为什么super会有这种效果?
下面就开始看看Python中的方法解析顺序MRO(Method Resolution Order)。
Python的类有一个__mro__属性,这个属性中就保存着方法解析顺序。结合上面的例子来看看类D的__mro__:
Python
1 2 3 | >>> print "MRO:", [x.__name__ for x in D.__mro__] MRO: ['D', 'B', 'C', 'A', 'object'] >>> |
看到这里,对于上面使用super例子的输出就应该比较清楚了。
- Python的多继承类是通过MRO的方式来保证各个父类的函数被逐一调用,而且保证每个父类函数只调用一次(如果每个类都使用super)
- 混用super类和非绑定的函数是一个危险行为,这可能导致应该调用的父类函数没有调用或者一个父类函数被调用多次
__slots__
从前面的介绍可以看到,当我们通过一个类创建了实例之后,仍然可以给实例添加属性,但是这些属性只属于这个实例。
有些时候,我们可以需要限制类实例对象的属性,这时就要用到类中的__slots__属性了。
__slots__属性对于一个tuple,只有这个tuple中出现的属性可以被类实例使用。
Python
1 2 3 4 5 6 7 8 9 | classStudent(object): __slots__=("name","age") def__init__(self,name,age): self.name=name self.age=age
s=Student("Wilber",28) print"%s is %d years old"%(s.name,s.age) s.score=96 |
在这个例子中,当场是给Student的实例s添加一个score属性的时候,就会遇到下面的异常:
子类没有__slots__属性
使用__slots__要注意,__slots__定义的属性仅对当前类的实例起作用,对继承的子类实例是不起作用的:
Python
1 2 3 4 5 6 7 8 9 10 11 12 | class Person(object): __slots__ = ("name", "age") pass
class Student(Person): pass
s = Student() s.name, s.age = "Wilber", 28 s.score = 100
print "%s is %d years old, score is %d" %(s.name, s.age, s.score) |
从代码的输出可以看到,子类Student的实例并不受父类中__slots__属性的限制:
子类拥有__slots__属性
但是,如果子类本身也有__slots__属性,子类的属性就是自身的__slots__加上父类的__slots__。
Python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | classPerson(object): __slots__=("name","age") pass
classStudent(Person): __slots__=("score",) pass
s=Student() s.name,s.age="Wilber",28 s.score=100
print"%s is %d years old, score is %d"%(s.name,s.age,s.score) prints.__slots__
s.city="Shanghai" |
代码的输出为:
python多重继承
There are two typical use cases forsuper
In a class hierarchy withsingle inheritance, super can be used to refer to parent classes withoutnaming them explicitly, thus making the code more maintainable. This useclosely parallels the use ofsuper in other programming languages.
The second use case is to support cooperative multiple inheritance in adynamic execution environment. This use case is unique to Python and isnot found in statically compiled languages or languages that only supportsingle inheritance. This makes it possible to implement “diamond diagrams”where multiple base classes implement the same method. Good design dictatesthat this method have the same calling signature in every case (because theorder of calls is determined at runtime, because that order adaptsto changes in the class hierarchy, and because that order can includesibling classes that are unknown prior to runtime).
For both use cases,a typical superclass call looks like this:
class C(B):
def method(self, arg):
super().method(arg) # This does the same thing as:super(C, self).method(arg)
[super
]
类多重继承实例
父类定义:
class Parent1(object):
def on_start(self):
print('do something')
class Parent2(object):
def on_start(self):
print('do something else')
class Parent3(object):
pass
子类定义1:
class Child(Parent1, Parent2, Parent3):
def on_start(self):
for base in Child.__bases__:
try:
base.on_start(self)
except AttributeError:
# handle that one of those does not have that method
print('"{}" does not have an "on_start"'.format(base.__name__))
Child().on_start() # c = Child(); c.on_start()
结果输出:
do something
do something else
"Parent3" does not have an "on_start"
子类定义2
class Child(Parent1, Parent2):
def on_start(self):
super(Child, self).on_start() # <=> super().on_start()
#super().method(arg) # This does the same thing as:super(C, self).method(arg)
super(Parent1, self).on_start()
class Child(Parent1, Parent2):
def on_start(self):
Parent1.on_start(self)
Parent2.on_start(self)
Child().on_start() # c = Child(); c.on_start()
两个结果都输出为:
do something
do something else
Note:
1. since both of the parents implements the same method, super
will just be the same as the first parent inherited, from left to right (for your code,Parent1
). Calling two functions withsuper
is impossible.
2. 注意子类定义2中的用法1:super(Parent1, self).on_start()为什么输出的是”do sth else"?
[Python Multiple Inheritance: call super on all]
注意的问题
按类名访问
就相当于C语言之前的GOTO
语句...乱跳,然后再用super
按顺序访问,就有问题了。
所以建议就是要么一直用super
,要么一直用按照类名访问
最佳实现:
- 避免多重继承
- super使用一致
- 不要混用经典类和新式类
- 调用父类的时候注意检查类层次
from:http://blog.csdn.net/pipisorry/article/details/46381341