DAY 12. python新式類和舊式類
繼承自object基類的類叫做新式類,否則叫做舊式類,python3中的類默認是新式類,之前版本默認是舊式類
root@kail:~# python
python 2.7.15 (default,Jul 28 2018,11:29:29)
[GCC 8.1.0] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> class A():
... pass
...
>>> a=A()
>>> dir(a)
['__doc__','__module__']
如上,在python2中定義一個類,不繼承任何基類,內建屬性只有兩個,這就是舊式類,如果想要創建一個新式類,需要顯式的繼承object基類,如:
root@kail:~# python
python 2.7.15 (default,Jul 28 2018,11:29:29)
[GCC 8.1.0] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> class A(object):
... pass
...
>>> dir(A)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__']
新式類默認有很多屬性,都是從object基類中繼承過來的,而在python3中所有類默認繼承object基類
Python 3.7.0 (v3.7.0:1bf9cc5093, Jun 27 2018, 04:59:51) [MSC v.1914 64 bit (AMD64)] on win32
Type "copyright", "credits" or "license()" for more information.
>>> class A:pass
>>> dir(A)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__']
>>>
每個屬性的具體用法參見Python——特殊屬性與方法
12.1 新式類和舊式類的區別
- 根本區別:新式類繼承自object基類,舊式類不繼承任何基類
- MRO不同:新式類和經典類的方法解析順序(MRO)不同,經典類使用DFS,新式類使用C3算法
python 2.7
class A():name = 'A'age = 12class B():name = 'B'class C():name = 'C'class D(A,B):name = 'D'class E(A,C):name = 'E'age = 13class F(D,E):passprint F.name # D
print F.age # 12
上面的繼承關系是:
顯然在age的繼承上,2.7使用了DFS,我們再看3.7
python 3.7
class A():name = 'A'age = 12class B():name = 'B'class C():name = 'C'class D(A,B):name = 'D'class E(A,C):name = 'E'age = 13class F(D,E):passprint(F.name) # D
print(F.age) # 13
顯然3.7沒有使用DFS,而是在每一次尋找入度為零的節點,加入mro列表后刪除這條邊,再次尋找,以上面的代碼為例,第一個入讀為0的節點就是F,所以mro表的第一項就是F,刪除F及相連的邊,入度為0的就是DE,按照書寫代碼的順序第二項為D,第三項為E,依次,最終所有新式類繼承自object,所以最后一項就是object,繼承順序就是按mro列表的順序來的,可以使用mro()查看mro列表
print(F.mro())
# [<class '__main__.F'>, <class '__main__.D'>, <class '__main__.E'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class 'object'>]
也就是說,C3是先在水平方向上查找,再往上查找
12.2 總結
基類 | MRO | 備注 | |
---|---|---|---|
經典類 | None | DFS | python2默認經典類 |
新式類 | object | C3 | python3默認新式類 |
我嘗試在python3中通過重寫type元類寫出經典類,但發現這樣寫出的類似乎不是真的經典類,只是默認屬性和經典類差不多,MRO行為依舊和新式類一樣,可能是我代碼有問題,請各位大佬賜教
class Type(type):__bases__ = ()__base__ = None__mro__ = (None,)Foo1 = Type('Foo1',() ,{})
Foo2 = Type('Foo2', (), {})
Foo3 = Type('Foo3', (), {})
Foo4 = Type('Foo4', (Foo1, Foo2), {})
Foo5 = Type('Foo5', (Foo1, Foo3), {})
Foo6 = Type('Foo6', (Foo4, Foo5), {})if __name__ == '__main__':# base 為空時會多出兩個屬性,第一個與類描述有關,第二個與弱拷貝有關print(dir(Foo1)) # ['__dict__', '__doc__', '__module__', '__weakref__']print(dir(Foo2)) # ['__dict__', '__doc__', '__module__', '__weakref__']print(dir(Foo3)) # ['__dict__', '__doc__', '__module__', '__weakref__']# base 不為空時,默認屬性表現的和經典類一樣print(dir(Foo4)) # ['__doc__', '__module__']print(dir(Foo5)) # ['__doc__', '__module__']print(dir(Foo6)) # ['__doc__', '__module__']# 假如定義的是經典類,這里應該不能調用mro方法,但這里調用了,說明本身就不對,并且mro列表最后是object,進一步說明這還是一個新式類print(Foo1.mro()) # [<class '__main__.Foo1'>, <class 'object'>]print(Foo2.mro()) # [<class '__main__.Foo1'>, <class 'object'>]print(Foo3.mro()) # [<class '__main__.Foo1'>, <class 'object'>]print(Foo4.mro()) # [<class '__main__.Foo4'>, <class '__main__.Foo1'>, <class '__main__.Foo2'>, <class 'object'>]print(Foo5.mro()) # [<class '__main__.Foo5'>, <class '__main__.Foo1'>, <class '__main__.Foo3'>, <class 'object'>]# 清楚的看到MRO使用的是C3算法print(Foo6.mro()) # [<class '__main__.Foo6'>, <class '__main__.Foo4'>, <class '__main__.Foo5'>, <class '__main__.Foo1'>, <class '__main__.Foo2'>, <class '__main__.Foo3'>, <class 'object'>]# TODO: 如何在python3中定義經典類,還是根本不能定義,拋磚引玉,請賜教