在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ Python/ 描述符
附錄
進(jìn)程通信
操作系統(tǒng)
迭代器
模塊
描述符
裝飾器
第三部分 擴(kuò)展庫(kù)
內(nèi)置類型
數(shù)據(jù)存儲(chǔ)
數(shù)據(jù)類型
基本環(huán)境
文件與目錄
異常
程序框架
數(shù)學(xué)運(yùn)算
函數(shù)
元類
字符串
表達(dá)式

描述符

很少有人會(huì)去刻意關(guān)注描述符 (Descriptor),盡管它時(shí)時(shí)刻刻以屬性、方法的身份出現(xiàn)。

描述符協(xié)議:

__get__(self, instance, owner) --> return value
__set__(self, instance, value)
__delete__(self, instance)

描述符對(duì)象以類型 (owner class) 成員的方式出現(xiàn),且最少要實(shí)現(xiàn)一個(gè)協(xié)議方法。最常見的描述符有 property、staticmethod、classsmethod。訪問描述符類型成員時(shí),解釋器會(huì)自動(dòng)調(diào)用與行為相對(duì)應(yīng)的協(xié)議方法。

  • 實(shí)現(xiàn) getset 方法,稱為 data descriptor。
  • 僅有 get 方法的,稱為 non-data descriptor。
  • get 對(duì) owner_class、owner_instance 訪問有效。
  • set、delete 僅對(duì) owner_instance 訪問有效。
>>> class MyDescriptor(object):
...     def __get__(self, instance, owner):  # 本例中 owner 是 class Data。
...         print "get:", instance, owner
...         return hex(id(instance))
...
...     def __set__(self, instance, value):
...         print "set:", instance, value
...
...     def __delete__(self, instance):
...         print "del:", instance

>>> class Data(object):
...     x = MyDescriptor()

>>> d = Data()

>>> d.x       # __get__ 的返回值。
get: <__main__.Data object at 0x107a23790> <class '__main__.Data'>
'0x107a23790'

>>> d.x = 100      # d 被當(dāng)做 instance 實(shí)參。
set: <__main__.Data object at 0x107a23790> 100

>>> del d.x       # d 被當(dāng)做 instance 實(shí)參。
del: <__main__.Data object at 0x107a23790>

>>> Data.x       # 以 owner 類型訪問時(shí),__get__ 有效。
get: None <class '__main__.Data'>   # instance = None
'0x106a96148'

>>> Data.x = 1      # __set__ 對(duì) class 調(diào)用無(wú)效。
        # 因此 Data.x 被重新賦值。

>>> type(Data.x)
<type 'int'>

如果沒有定義 get 方法,那么直接返回描述符對(duì)象,不會(huì)有默認(rèn) get 實(shí)現(xiàn)。

property

屬性總是 data descriptor,這和是否提供 setter 無(wú)關(guān)。其優(yōu)先級(jí)總是高過(guò)同名實(shí)例字段,如果沒有提供 setter,set 方法會(huì)阻止賦值操作。

>>> class Data(object):
...     oid = property(lambda s: hex(id(s)))

>>> hasattr(Data.oid, "__set__")
True

>>> d = Data()

>>> d.oid
'0x107a23a90'

>>> d.oid = 123
AttributeError: can't set attribute

non-data

non-data descriptor 會(huì)被同名實(shí)例字段搶先。

>>> class Descriptor(object):
...     def __get__(self, instance, owner):
...         print "__get__"

>>> class Data(object):
...     x = Descriptor()

>>> d = Data()

>>> d.x    # 描述符有效。
__get__

>>> d.__dict__   # instance.__dict__ 沒有同名字段。
{}

>>> d.x = 123   # 沒有 __set__,創(chuàng)建同名實(shí)例字段。

>>> d.__dict__  
{'x': 123}

>>> d.x    # 依據(jù)成員查找規(guī)則,實(shí)例字段被優(yōu)先命中。
123

>>> Data.x    # 描述符在 owner_class.__dict___。
__get__

bound method

通過(guò)描述符,我們可以了解實(shí)例方法 self 參數(shù)是如何隱式傳遞的。

>>> class Data(object):
...     def test(self): print "test"

>>> d = Data()

>>> d.test      # 只有 bound method 才會(huì)隱式傳遞 self。
<bound method Data.test of <__main__.Data object at 0x10740b050>>

>>> Data.test.__get__(d, Data)   # 向 __get__ 傳遞 instance 參數(shù)。
<bound method Data.test of <__main__.Data object at 0x10740b050>>

>>> Data.test     # unbound method 需顯式傳遞 self。
<unbound method Data.test>

>>> Data.test.__get__(None, Data)  # instance 為 None。
<unbound method Data.test>

現(xiàn)在可以看出,bound/unbound 是 get 造成的,關(guān)鍵就是 instance 參數(shù)。那么 self 參數(shù)存在哪?由誰(shuí)替我們自動(dòng)傳遞 self 參數(shù)呢?

>>> bm = Data.test.__get__(d, Data)

>>> bm.__func__     # 實(shí)際的目標(biāo)函數(shù) test。
<function test at 0x107404488>

>>> bm.__self__     # __get__ instance 參數(shù),也就是 self。
<__main__.Data object at 0x10740b050>

>>> bm.__call__()     # __call__ 內(nèi)部替我們傳遞 self !
test

>>> unbm = Data.test.__get__(None, Data) # unbound method

>>> unbm.__func__
<function test at 0x107404488>

>>> unbm.__self__ is None   # instance == None, self == None。
True

>>> unbm.__call__()    # __call__ 會(huì)檢查 __self__。
TypeError: unbound method test() must be called with Data instance as first argument
(got nothing instead)

>>> unbm.__call__(d)    # 只好給 __call__ 有效的 instance。
test

classmethod

不同于 staticmethod,classmethod 會(huì) bound 類型對(duì)象。

>>> class Data(object):
...     @classmethod
...     def test(cls): print cls

>>> Data.test.__get__(None, Data)
<bound method type.test of <class '__main__.Data'>>

>>> m = Data.test.__get__(None, Data)

>>> m.__self__     # 類型對(duì)象,也就是隱式 cls 參數(shù)。
<class '__main__.Data'>

>>> m.__call__()
<class '__main__.Data'>