Reputation: 113
Four references to a class are produced when a class is created which that never had a __classcell__
passed in the namespace argument of type.__new__
, aka a class with no functions that ever need the opcode LOAD_NAME - __class__
. I can account for one, but since I can barely read C I can only speculate about the other 3. Most obviously, the first is the reference in the __dict__
of the module the class was defined in. Another I think one is in a cache in typeobject.c
. After a while if the reference count of a class drops below 4 it is destroyed, which is a good thing for classes defined in in function since there's no way to delete it after the function has been executed.
Also there is perhaps a reference in whatever type.mro
uses to generate mros.
Finally, I'm guessing the last is somewhere zero argument super
can access, and that is the real info I'm after: why does a function created outside a class definition not work with zero argument super
, even when it has '__class__'
in its __code__.co_names
and a cell with the class in its __closure__
? A RuntimeError: super(): __class__ cell not found
is still produced.
Classes that had a __classcell__
(I say had because I believe that name is eventually deleted) have 5 references upon creation, one more than not. Seeing as the cell containing the class object in the __closure__
of all functions that mention zero argument super()
or __class__
are identical I think it's a good bet that the additional reference is used by the types.FunctionType.__closure__
descriptor to produce the tuple
containing the cells.
Upvotes: 4
Views: 84
Reputation: 280733
Let's take a look with gc.get_referrers
:
>>> import gc
>>> import pprint
>>> class Foo: pass
...
>>> pprint.pprint(gc.get_referrers(Foo))
[<attribute '__dict__' of 'Foo' objects>,
<attribute '__weakref__' of 'Foo' objects>,
(<class '__main__.Foo'>, <class 'object'>),
{'Foo': <class '__main__.Foo'>,
'__annotations__': {},
'__builtins__': <module 'builtins' (built-in)>,
'__cached__': None,
'__doc__': None,
'__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x7ff284402da0>,
'__name__': '__main__',
'__package__': None,
'__spec__': None,
'gc': <module 'gc' (built-in)>,
'pprint': <module 'pprint' from '/usr/local/lib/python3.6/pprint.py'>}]
We can see in this list that the 3 references you were missing are from the __dict__
and __weakref__
descriptors and the class's __mro__
. (It might not be obvious that the third entry is the __mro__
, but we can confirm:)
>>> gc.get_referrers(Foo)[2] is Foo.__mro__
True
The __dict__
and __weakref__
descriptors manage access to the __dict__
and __weakref__
attributes of Foo
instances, and they need a reference to Foo
for type checking, to make sure they're only used on Foo
instances.
The __mro__
is the sequence of classes Python searches to resolve class attributes of Foo
, and to resolve instance attributes of Foo
which are managed by a data descriptor or which don't have an entry in the instance __dict__
.
Upvotes: 3