Python函數是descriptors。您可以通過簡單地看它的dir
檢查:
>>> def fn(self):
... pass
...
>>> dir(fn)
['__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__doc__', '__format__', '__get__', '__getattribute__', '__globals__', '__hash__', '__init__', '__module__', '__name__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'func_closure', 'func_code', 'func_defaults', 'func_dict', 'func_doc', 'func_globals', 'func_name']
>>> fn.__get__
<method-wrapper '__get__' of function object at 0x7fa5585550c8>
通知的__get__
method。我們可以用它來生成綁定方法:
>>> class Bar(object): pass
...
>>> b = Bar()
>>> fn.__get__(b, Bar)
<bound method Bar.fn of <__main__.Bar object at 0x7fa5585cdcd0>>
其實,這是蟒蛇會自動爲你做,當你在一個類,這是一個函數訪問的屬性。
現在,關於緩存的問題 - 函數可以放在任何地方的類上。我可以像上面那樣向任何類添加綁定方法 - 簡單分配b.fn = fn.__get__(b, Bar)
,現在實例b
具有綁定方法fn
,即使Bar
的其他實例沒有該方法。我可以爲多個班級做這個。如果描述符要緩存這些值,則需要保留一個查找表,查找實例和類以查看它是否已經爲該實例和類創建了綁定方法。這裏有一個簡單的例子:
def fn(self):
return self
class A(object):
fn = fn
class B(object):
fn = fn
注意A
和B
持有相同的函數的引用 - 並在必要時屬性上的實例訪問他們都會產生綁定方法。
由於self
可能不是可破解的,所以查找表首先構建起來會產生問題。我認爲這可能是每次創建新實例的主要原因。 即使不是這種情況,查找表將需要存儲弱引用 - 而不是實際的引用 - 這樣整個事情可以在必要時被垃圾收集。 我猜創建一個新的綁定方法可能與使用weakrefs進行查找和解析時一樣快。