Python 2.2 起類型和類獲得了統一,標準的數據類型開始能夠子類化,不過也所以,本來的轉換函數如今都變成了工廠函數。如 int() 、list() 等,他們的行爲變成了生成一個類型對象的實例,雖然用起來還像個函數似的。這帶來的一個小的便利之處是,對變量的類型判斷有了更好的實現方法:python
>>> type(123) is int True >>> isinstance(123,int) True >>> isinstance(123.0,(int,float)) True
這裏須要注意的是:isinstance() 函數對於子類的實例也會返回 True,因此若是想精確斷定,請使用 is 。shell
每一個類對象中都有個 __dict__ 屬性,他使用字典來存儲這個對象內全部(可寫的)屬性。安全
__slots__ 是一個序列類型的對象,他能夠是列表、元組,或其餘可迭代對象。當類屬性中定義了 __slots__ 時,__dict__ 會被排斥而不存在。__slots__ 裏面存儲的也是類的屬性,他和 __dict__ 的區別在於:函數
class C(object): __slots__=['a'] >>> c=C() >>> c.a=1 >>> c.b=0 Traceback (most recent call last): File "<pyshell#196>", line 1, in <module> c.b=0 AttributeError: 'C' object has no attribute 'b'
注:__slots__ 也能夠是一個字符串,若是這樣定義的話這個類就只容許有這一個屬性。ui
在上一篇的「受權」中曾提到過一個 __getattr__() 特殊方法,它僅在找不到屬性時被調用。而這裏的 __getattribute__() 則老是被調用。當兩者同時存在時,除非 __getattribute__() 明確調用後者,或其引起了 AttributeError,不然 __getattr__() 不會被調用。3d
__getattribute__() 在搜尋屬性時有以下優先級:代理
這個順序的具體應用和描述符的解釋在後面:code
前面提到的 __getattr__() 和 __getattribute__() 均可以用來將對對象的屬性訪問重定向到其餘地方。而描述符的做用則是,自定義「對象的屬性訪問」行爲自己。描述符「描述」的是對象的屬性——將對屬性的訪問(get)、賦值(set)和刪除(delete)行爲代理起來,由三個函數來從新實現。若是隻設置了 getter,則這是一個只讀屬性。所以,描述符其實是一個實現了 __get__(), __set__(), __delete__() 這三個特殊方法(描述符協議)的類,並被賦值給某個對象的屬性。orm
上面的參數裏,self 都是指描述符自己,owner 是包含描述符爲某個屬性的那個對象,instance 是 owner 的實例。這些參數由解釋器自動傳遞,沒有就傳 None。函數體由用戶自定義,此處能夠妥善使用三個參數(self, instance, owner)。因爲這裏的描述符是 owner 的類屬性,因此若是你使用 self.xxx 來存儲屬性值的話,這個屬性也會被存成類屬性,即你全部的實例都將訪問同一個描述符:對象
class Descriptor(object): def __init__(self): self.d_name = '' def __get__(self, instance, owner): return self.d_name def __set__(self, instance, name): self.d_name = name.title() def __delete__(self, instance): del self.d_name class Person(object): name = Descriptor()
這裏咱們使用描述符的方式,是保存一個字符串,並把首字母大寫,實際描述符能夠作得更多。運行以下:
>>> a = Person() >>> a.name = 'adam' >>> a.name 'Adam' >>> b = Person() >>> b.name 'Adam'
這就是問題所在,若是想把描述符用做實例屬性而不是類屬性的話,使用 instance 參數代替 self 就行了。
除了上面這種使用類來實現描述符的方式外,Python 還提供了一個 property() 內建函數。本函數接受【fget(), fset(), fdel(), doc】三個函數和一個文檔字符串做爲參數,並返回一個 property 對象(描述符)。描述符協議由那三個函數參數來實現:
property(fget=None,fset=None,fdel=None,doc=None)
fget(self), fset(self,val), fdel(self) 這三個函數都默認接受 self 參數,這指的是實現 property 屬性的類的實例,fset 還多接受一個 val 參數。因此通常來講,property 用來實現實例屬性,除非你刻意使用 type(self) 這樣的語句來訪問類屬性:
class Person(object): def fget(self): return self.val def fset(self,val): self.val = val name = property(fget,fset,doc='its a name property.')
運行以下:
>>> a = Person() >>> a.name = 'John' >>> a.name 'John' >>> Person.name.__doc__ 'its a name property.' >>> a.val 'John'
這裏沒辦法在實例裏如‘a.name.__doc__’般引用文檔字符串是由於‘a.name’會直接調用 fget() 。另外這種在類裏面定義三個函數的方法有可能帶來污染命名空間的困擾,所以在 ASPN上曾有人提出過一種解決方法,仍是上面那個 Person 的例子:
class Person(object): def name(): def fget(self): return self.val def fset(self,val): self.val = val return locals() name = property(**name())
運行結果和上面的同樣。或者還有一種方法,就是使用 property 提供的裝飾器:
class Person(object): @property def name(self): return self.val @name.setter def name(self,val): self.val = val @name.deleter def name(self): del self.val
這裏第一個被 @property 裝飾的是 fget 函數,隨後使用 name 的 setter 和 deleter 方法來裝飾 fset 和 fdel。其實 property.getter() 方法也是有的,不過先定義 getter 比較方便(由於是第一個參數,沒必要使用關鍵字參數來傳),並且 getter 實現的機會比另兩個更多。來看一下裝飾器的 getter 和 setter:
>>> Person.name.setter <built-in method setter of property object at 0x0000000009E4D5E8> >>> Person.name.getter <built-in method getter of property object at 0x0000000009E4D5E8>