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进行查找和解析时一样快。