Search code examples
pythonweak-referencesrepr

Where to find __repr__ of Python weakref proxy?


I am a bit puzzled right now about the following:

import weakref

class A:
    def __init__(self, p):
        self.p = p
        
    def __repr__(self):
        return f"{type(self).__name__}(p={self.p!r})"
    
a = A(1)
proxy_a = weakref.proxy(a)

print(repr(proxy_a))
# '<weakproxy at 0x7f2ea2fc1b80 to A at 0x7f2ea2fee610>'
print(proxy_a.__repr__())
# 'A(p=1)'

Why does repr(proxy_a) return a representation of the proxy while proxy_a.__repr__() returns the representation of the original object? Shouldn't the two calls boil down to the same thing? And which __repr__ implementation is actually called by using repr(proxy_a)?


Solution

  • repr(proxy_a) calls the default C implementation of repr for the weakref.proxy object. While proxy_a.__repr__() proxies the version from the a object.

    Yes I would expect them to execute the same code, but wait, don't we expect also the proxy to send attribute lookups and method calls to the proxied object? At the same time I would want to see that a proxy is a proxy object, so the repr(proxy_a) result makes sense too. So it is not even clear what should be the right behaviour.

    Information on this is very scarce, but it looks weakref.proxy objects do not replace in a complete transparent way the original objects, contrary to common expectations.

    Added a few print lines to make things more clear. Note in the last line, that it is possible to access the weakreferred object and its methods through the __self__ parameter of a bound method from the proxy.

    import weakref
    
    class A:
        def __init__(self, p):
            self.__p = p
            
        def __repr__(self):
            return f"{type(self).__name__}(p={self.__p!r})"
    
    a = A(1)
    proxy_a = weakref.proxy(a)
    
    print(repr(proxy_a))
    # '<weakproxy at 0x7f2ea2fc1b80 to A at 0x7f2ea2fee610>'
    print(proxy_a.__repr__())
    # 'A(p=1)'
    print(proxy_a.__repr__)
    # <bound method A.__repr__ of A(p=1)>
    print(type(proxy_a))
    # <class 'weakproxy'>
    print(type(proxy_a.__repr__.__self__))
    # <class '__main__.A'>
    print(proxy_a.__repr__.__self__.__repr__())
    # A(p=1)
    

    See also this complete thread python-dereferencing-weakproxy and some of the entries in this (old) thread from the Python bugtracker, where weakref.proxy and method delegation is mentioned in several places.