18

Python 2.7 docs for weakref module say this:

Not all objects can be weakly referenced; those objects which can include class instances, functions written in Python (but not in C), methods (both bound and unbound), ...

And Python 3.3 docs for weakref module say this:

Not all objects can be weakly referenced; those objects which can include class instances, functions written in Python (but not in C), instance methods, ...

To me, these indicate that weakrefs to bound methods (in all versions Python 2.7 - 3.3) should be good, and that weakrefs to unbound methods should be good in Python 2.7.

Yet in Python 2.7, creating a weakref to a method (bound or unbound) results in a dead weakref:

>>> 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

Not what I would have expected based on the docs.

Similarly, in Python 3.3, a weakref to a bound method dies on creation:

>>> wr=weakref.ref(Foo.bar, isDead)
>>> wr() is None
False
>>> foo=Foo()
>>> wr=weakref.ref(foo.bar, isDead)
dead!
>>> wr() is None
True

Again not what I would have expected based on the docs.

Since this wording has been around since 2.7, it's surely not an oversight. Can anyone explain how the statements and the observed behavior are in fact not in contradiction?

Edit/Clarification: In other words, the statement for 3.3 says "instance methods can be weak referenced"; doesn't this mean that it is reasonable to expect that weakref.ref(an instance method)() is not None? and if it None, then "instance methods" should not be listed among the types of objects that can be weak referenced?

4

3 に答える 3

3

なぜそうあるべきかについては受け入れられた答えがあると思いますが、バインドされたメソッドへの弱参照として機能するオブジェクトが必要な単純なユースケースの状況から、そのようなオブジェクト。そこにある「コーディエ」のものと比べると、ちょっとしたものですが、うまくいきます.

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')代わりにアプローチを使用する必要があるかもしれません。getattrYMMV.

于 2014-06-18T13:54:26.850 に答える