Python的类实例对象具有属性。这些属性可以是数据属性或方法。
让我们以这个类为例
class Foo:
def bar(self):
print("FooBar")
让我们创建一个类实例
fooInstance = Foo()
当我阅读文档并亲自测试时,我意识到 每次我在代码中引用或每次我从实例调用 bar 时
fooInstance.bar
每次都会在内部创建一个方法对象。
我的问题是为什么它不被缓存和重用
我的一个假设是因为方法对象的可调用对象可能会以某种方式在内部更改地址(即由于重新分配?)并且指向它的指针可能会失效。
抱歉,如果这个问题“愚蠢”,我对Python完全陌生,所以我对它的了解很少,甚至更少了解它的内部结构。
最佳答案
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 。我们可以用它来生成绑定(bind)方法:
>>> class Bar(object): pass
...
>>> b = Bar()
>>> fn.__get__(b, Bar)
<bound method Bar.fn of <__main__.Bar object at 0x7fa5585cdcd0>>
事实上,当您访问作为函数的类的属性时,Python 会自动为您执行此操作。
现在,讨论缓存问题——函数可以从任何地方放入类中。我可以像上面那样向任何类添加绑定(bind)方法 - 简单地分配 b.fn = fn.__get__(b, Bar)
,现在实例 b
具有绑定(bind)方法 fn
,即使 Bar
的其他实例都没有该方法。我可以为多个类(class)执行此操作。如果描述符要缓存值,则需要保留一个查找表来查找实例和类,以查看它是否已经为该实例和类创建了绑定(bind)方法。这是一个更简单的示例:
def fn(self):
return self
class A(object):
fn = fn
class B(object):
fn = fn
请注意,A
和 B
持有对同一函数的引用 - 当在实例上访问该属性时,它们都会根据需要生成绑定(bind)方法。
查找表的构建首先存在问题,因为 self
可能不可散列。我认为这可能是每次创建新实例的主要原因。 即使情况并非如此,查找表也需要存储弱引用(而不是实际引用),以便在必要时可以对整个内容进行垃圾收集。 我猜测创建一个新的绑定(bind)方法可能几乎与使用弱引用时进行查找和解析一样快。
关于Python方法对象创建,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25537069/