GIZ
GIZ

Reputation: 4633

Why is class.__weakref__ not None, while instance.__weakref__ is None?

__weakref__ is related to weak references. I get the whole idea behind weak references and where I might use them. The only thing that I don't get is described in the following:

An instance doesn't have the attribute __weakref__ itself, distinct from the class, therefore the instance inherits __weakref__ from the class, this means that A.__weakref__ should be the same as A().__weakref__:

>>> class A: pass
...
>>> A().__dict__            # Each instance starts out as an empty namespace 
{}
>>> A.__weakref__ is None; 
False
>>> A().__weakref__ is None   #But this is True!
True 

Why is A.__weakref__ not None while instance.__weakref__ is None although instances inherit __weakref__ from the class?

Upvotes: 4

Views: 477

Answers (1)

Martijn Pieters
Martijn Pieters

Reputation: 1122372

A class has a __weakref__ data descriptor attribute; this acts just like a property; only when you access the attribute on an instance is it automatically bound. The actual data for a weak reference is stored in a C structure, part of the data structure Python uses to represent classes and instances in memory.

As such, instances don't need their own __weakref__ attribute. The class descriptor is bound to the instance data structure, and the C code then just looks in the right C struct to retrieve the information needed.

Accessing the attribute on the class, produces the descriptor object itself. This is not None; it is the descriptor object. On an instance, the bound attribute produces the weak reference. No weak reference means None is returned.

You can re-create the descriptor behaviour by accessing the object via A.__dict__['__weakref__'] (to bypass the normal type.__getattribute__() binding behaviour), then directly calling __get__ on that:

>>> import weakref
>>> class A(object): pass
...
>>> a = A()
>>> A.__weakref__
<attribute '__weakref__' of 'A' objects>
>>> descriptor = A.__dict__['__weakref__']
>>> descriptor.__get__(None, A)
<attribute '__weakref__' of 'A' objects>
>>> a = A()
>>> a.__weakref__ is None
True
>>> descriptor.__get__(a) is None
True
>>> wr = weakref.ref(a)  # add a weak reference
>>> wr
<weakref at 0x10bd86d68; to 'A' at 0x10bad3588>
>>> a.__weakref__
<weakref at 0x10bd86d68; to 'A' at 0x10bad3588>
>>> descriptor.__get__(a)
<weakref at 0x10bd86d68; to 'A' at 0x10bad3588>

Upvotes: 5

Related Questions