標籤:技術分享 父類 __weak 語句 line 廣度 import metaclass lte
擷取對象資訊
拿到一個變數,除了用 isinstance() 判斷它是否是某種類型的執行個體外,還有沒有別的方法擷取到更多的資訊呢?
例如,已有定義:
class Person(object): def __init__(self, name, gender): self.name = name self.gender = genderclass Student(Person): def __init__(self, name, gender, score): super(Student, self).__init__(name, gender) self.score = score def whoAmI(self): return 'I am a Student, my name is %s' % self.name
首先可以用 type() 函數擷取變數的類型,它返回一個 Type 對象:
>>> type(123)<type 'int'>>>> s = Student('Bob', 'Male', 88)>>> type(s)<class '__main__.Student'>
其次,可以用 dir() 函數擷取變數的所有屬性:
>>> dir(123) # 整數也有很多屬性...['__abs__', '__add__', '__and__', '__class__', '__cmp__', ...]>>> dir(s)['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'gender', 'name', 'score', 'whoAmI']
對於執行個體變數,dir()返回所有執行個體屬性,包括__class__這類有特殊意義的屬性。注意到方法whoAmI也是 s 的一個屬性。
如何去掉__xxx__這類的特殊屬性,只保留我們自己定義的屬性?回顧一下filter()函數的用法。
dir()返回的屬性是字串列表,如果已知一個屬性名稱,要擷取或者設定對象的屬性,就需要用 getattr() 和 setattr( )函數了:
>>> getattr(s, 'name') # 擷取name屬性'Bob'>>> setattr(s, 'name', 'Adam') # 設定新的name屬性>>> s.name'Adam'>>> getattr(s, 'age') # 擷取age屬性,但是屬性不存在,報錯:Traceback (most recent call last): File "<stdin>", line 1, in <module>AttributeError: 'Student' object has no attribute 'age'>>> getattr(s, 'age', 20) # 擷取age屬性,如果屬性不存在,就返回預設值20:20
新式類和舊式類
python的新式類是2.2版本引進來的,我們可以將之前的類叫做經典類或者舊類。
為什麼要在2.2中引進new style class呢?官方給的解釋是:為了統一類(class)和類型(type)。
在2.2之前,比如2.1版本中,類和類型是不同的,
如a是ClassA的一個執行個體,
那麼a.__class__返回class __main__.ClassA,type(a)返回總是<type ‘instance‘>。
而引入新類後,比如ClassB是個新類,b是ClassB的執行個體,b.__class__和type(b)都是返回‘class ‘__main__.ClassB‘,這樣就統一了。
引入新類後,還有其他的好處,比如更多的內建屬性將會引入,描述符的引入,屬性可以來計算等等。為了向前相容,預設情況下使用者定義的類為經典類,新類需要繼承自所有類的基類 object 或者繼承自object的新類。
? 值得注意的地方是,雖然使用的是最新的python(2.7),但是一些特性不會在舊式類起作用。
? 所以,為了確保自己使用的是新式類,有以下方法:
? 把這個指派陳述式放在類別模組代碼的最前面__metaclass__ = type(前面有提過)。
? 自己的類都從內建類object直接或者間接地繼承。
? 如果不需要相容舊式類,舊版本的類,那麼就保持都是新式類。
? 當然,在Python3裡面,不存在這些問題了,因為所有的類都是object類的子類(隱式)。
# encoding:utf-8# 新式類和舊式類的對比class OldStyle: def __init__(self, name, description): self.name = name self.description = description#新類,可以在這裡加 __metaclass__ = type class NewStyle(object): #新類,也可以直接繼承至object類 def __init__(self, name, description): self.name = name self.description = descriptionif __name__ == '__main__': old = OldStyle('old', 'Old style class') print(old.__class__) print((type(old))) print((dir(old))) print('-------------------------------------------------------') new = NewStyle('new', 'New style class') print(new.__class__) print((type(new))) print((dir(new)))
運行結果:
__main__.OldStyle<type 'instance'>['__doc__', '__init__', '__module__', 'description', 'name']-------------------------------------------------------<class '__main__.NewStyle'><class '__main__.NewStyle'>['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'description', 'name'][Finished in 0.1s]
多繼承屬性尋找機制
如果將每個類看成一個點,眾多的類就構成一張圖,那麼Python中繼承的機制是就是一種搜尋
那麼地,這種搜尋是深度優先搜尋,還是廣度優先搜尋呢?
上述的問題也叫做mro即method resolution order,主要用於在多繼承時判斷調的屬性的路徑(來自於哪個類)。
在舊式類(在Python2.3之前)之中類的多繼承機制是基於深度優先搜尋,但在新式類(從Python2.3起)之中類的多繼承機制是基於類似於廣度優先搜尋的C3演算法。
C3演算法
C3演算法最早被提出是用於Lisp的,應用在Python中是為瞭解決原來基於深度優先搜尋演算法不滿足本地優先順序,和單調性的問題。
- 本地優先順序:指聲明時父類的順序,比如C(A,B),如果訪問C類對象屬性時,應該根據聲明順序,優先尋找A類,然後再尋找B類。
- 單調性:如果在C的解析順序中,A排在B的前面,那麼在C的所有子類裡,也必須滿足這個順序。
在新式類中,尋找一個要調用的函數或者屬性的時候,是廣度優先搜尋的。
在舊式類當中,是深度優先搜尋的。如所示:
Python中幾種提供繼承順序的方法:
mro方法
__mro__屬性
inspect模組的getmro方法
import inspectclass A: passclass B(A): passprint(B.mro())print(B.__mro__)print(inspect.getmro(B))
運行結果:
[<class '__main__.B'>, <class '__main__.A'>, <class 'object'>](<class '__main__.B'>, <class '__main__.A'>, <class 'object'>)(<class '__main__.B'>, <class '__main__.A'>, <class 'object'>)
從上結果來看,以元組為結果的__mro__和inspect.getmro看來更讓人滿意
深度優先和
廣度優先
# -*- coding:utf-8 -*-from inspect import getmroclass D: passclass B(D): passclass C(D): passclass A(B, C): passif __name__ == '__main__': print(getmro(A))
運行結果:
(<class __main__.A at 0x0000000002CEC888>, <class __main__.B at 0x0000000002CECE88>,<class __main__.D at 0x0000000002CEC8E8>, <class __main__.C at 0x0000000002CECD68>)[Finished in 0.1s]
# -*- coding:utf-8 -*-from inspect import getmroclass D(object): passclass B(D): passclass C(D): passclass A(B, C): passif __name__ == '__main__': print(getmro(A))
運行結果:
(<class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.D'>, <type 'object'>)[Finished in 0.1s]
Python物件導向-擴充