用于weakref模块的Python 2.7文档说:
并非所有对象都可以被弱引用;那些可以的物体
包括类实例,用Python编写的函数(但不是在C中),
方法(绑定和非绑定),...
而对于weakref模块的Python 3.3文档说:
并非所有对象都可以被弱引用;那些可以的物体
包括类实例,用Python编写的函数(但不是在C中),
实例方法,...
对我来说,这些表明对绑定方法的弱化(在所有版本的Python 2.7 - 3.3中)应该是好的,并且对于未绑定的方法的弱引用应该在Python 2.7中很好。
然而在Python 2.7中,为方法(绑定或未绑定)创建弱参数会导致死弱点:
>>> def isDead(wr): print 'dead!'
...
>>> class Foo:
... def bar(self): pass
...
>>> wr=weakref.ref(Foo.bar, isDead)
dead!
>>> wr() is None
True
>>> foo=Foo()
>>> wr=weakref.ref(foo.bar, isDead)
dead!
>>> wr() is None
True
不是我根据文档所期望的。
类似地,在Python 3.3中,绑定方法的弱参数在创建时死亡:
>>> wr=weakref.ref(Foo.bar, isDead)
>>> wr() is None
False
>>> foo=Foo()
>>> wr=weakref.ref(foo.bar, isDead)
dead!
>>> wr() is None
True
再也不是我根据文档所期望的那样。
由于这个措辞自2.7以来一直存在,因此肯定不是疏忽。任何人都可以解释这些陈述和观察到的行为是如何实际的 不 矛盾吗?
编辑/澄清:换句话说,3.3的陈述说“实例方法可能被弱引用”;这是不是意味着期望weakref.ref(一个实例方法)()不是None是合理的?如果它是None,那么“实例方法”不应该列在可以弱引用的对象类型中?
Foo.bar
每次访问它时都会生成一个新的未绑定方法对象,因为有一些关于描述符的详细信息以及如何在Python中实现这些方法。
该类没有未绑定的方法;它拥有功能。 (查看 Foo.__dict__['bar']
。)这些功能碰巧有一个 __get__
返回一个unbound-method对象。由于没有别的东西可以引用,所以一旦你创建了weakref,它就会消失。 (在Python 3中,相当不必要的额外层消失了,“未绑定方法”只是底层函数。)
绑定方法的工作方式几乎相同:函数 __get__
返回一个bound-method对象,实际上就是这样 partial(function, self)
。你每次都会得到一个新的,所以你会看到同样的现象。
您可以存储方法对象并保留对它的引用 那, 当然:
>>> def is_dead(wr): print "blech"
...
>>> class Foo(object):
... def bar(self): pass
...
>>> method = Foo.bar
>>> wr = weakref.ref(method, is_dead)
>>> 1 + 1
2
>>> method = None
blech
这一切看起来都是可疑的,但:)
请注意,如果是Python 没有 在每个属性访问上吐出一个新的方法实例,这意味着类引用它们的方法和方法引用它们的类。在整个程序中的每个单个实例上具有这样的循环将进行垃圾收集 办法 更昂贵 - 在2.1之前,Python甚至没有循环收集,所以他们会永远陷入困境。
@ Eevee的回答是正确的,但有一个重要的微妙之处。
Python文档声明实例方法(py3k)和un / bound方法(py2.4 +)可以被弱引用。你会(天真,就像我一样)期待 weakref.ref(foo.bar)()
因此,它将是非,但它是无,使弱的参考“在抵达时死亡”(DOA)。这导致我的问题,如果实例方法的弱参数是DOA,为什么文档说你可以弱一个方法?
所以@Eevee表明,你 能够 创建一个 非-dead对实例方法的弱引用,通过创建对weakref提供的方法对象的强引用:
m = foo.bar # creates a *new* instance method "Foo.bar" and strong refs it
wr = weakref.ref(m)
assert wr() is not None # success
微妙(对我来说,无论如何)是一个 新 实例方法对象已创建 一切 你使用Foo.bar的时间,所以即使在运行上面的代码之后,以下代码也会失败:
wr = weakref.ref(foo.bar)
assert wr() is not None # fails
因为foo.bar是 新 “Foo实例”foo的“bar”方法的实例,与m不同,并且没有对这个新实例的强烈引用,所以它立即被gc'd,即使你之前已经创建了一个强引用(它是不一样强烈的参考)。要清楚,
>>> d1 = foo.bla # assume bla is a data member
>>> d2 = foo.bla # assume bla is a data member
>>> d1 is d2
True # which is what you expect
>>> m1 = foo.bar # assume bar is an instance method
>>> m2 = foo.bar
>>> m1 is m2
False # !!! counter-intuitive
这让很多人感到意外,因为没有人希望访问实例成员来创建任何新的实例。例如,如果foo.bla是foo的数据成员,那么在代码中使用foo.bla不会创建foo.bla引用的对象的新实例。现在,如果bla是一个“函数”,foo.bla会创建一个表示绑定函数的“实例方法”类型的新实例。
为什么weakref docs(因为python 2.4!)没有指出它是非常奇怪的,但这是一个单独的问题。
虽然我看到有一个公认的答案 为什么 这应该是这样,从一个简单的用例情况,其中一个人希望一个对象作为一个弱的参数作为一个绑定的方法,我相信一个人可能能够与一个对象偷偷摸摸。与某些“更健康”的东西相比,这是一种傻瓜,但它有效。
from weakref import proxy
class WeakMethod(object):
"""A callable object. Takes one argument to init: 'object.method'.
Once created, call this object -- MyWeakMethod() --
and pass args/kwargs as you normally would.
"""
def __init__(self, object_dot_method):
self.target = proxy(object_dot_method.__self__)
self.method = proxy(object_dot_method.__func__)
###Older versions of Python can use 'im_self' and 'im_func' in place of '__self__' and '__func__' respectively
def __call__(self, *args, **kwargs):
"""Call the method with args and kwargs as needed."""
return self.method(self.target, *args, **kwargs)
作为其易用性的一个例子:
class A(object):
def __init__(self, name):
self.name = name
def foo(self):
return "My name is {}".format(self.name)
>>> Stick = A("Stick")
>>> WeakFoo = WeakMethod(Stick.foo)
>>> WeakFoo()
'My name is Stick'
>>> Stick.name = "Dave"
>>> WeakFoo()
'My name is Dave'
请注意,邪恶的欺骗会导致这种情况爆发,所以根据你喜欢它的工作方式,这可能不是最佳解决方案。
>>> A.foo = lambda self: "My eyes, aww my eyes! {}".format(self.name)
>>> Stick.foo()
'My eyes, aww my eyes! Dave'
>>> WeakFoo()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 6, in __call__
ReferenceError: weakly-referenced object no longer exists
>>>
如果您要在运行中替换方法,则可能需要使用 getattr(weakref.proxy(object), 'name_of_attribute_as_string')
改为接近。 getattr
是一个相当快速的查找,所以这不是世界上最糟糕的事情,但取决于你正在做什么,YMMV。