简体   繁体   中英

dict does not reference elements? Python2.7 changed behavior

Given the example:

>>> import gc
>>> d = { 1 : object() }
>>> gc.get_referrers(d[1])
[] # Python 2.7
[{1: <object object at 0x003A0468>}] # Python 2.5

Why is d not listed as refererrer to to the object?

EDIT1: Although the dict in d references the object, why is the dictionairy not listed?

The doc mentions that:

This function will only locate those containers which support garbage collection; extension types which do refer to other objects but do not support garbage collection will not be found.

Seems that dictionaries do not support it.

And here is why:

The garbage collector tries to avoid tracking simple containers which can't be part of a cycle. In Python 2.7, this is now true for tuples and dicts containing atomic types (such as ints, strings, etc.). Transitively, a dict containing tuples of atomic types won't be tracked either. This helps reduce the cost of each garbage collection by decreasing the number of objects to be considered and traversed by the collector.

— From What's new in Python 2.7

It seems that object() is considered an atomic type , and trying this with an instance of a user-defined class (that is, not object ) confirms this as your code now works.

# Python 2.7
>>> class A(object): pass
>>> r = A()
>>> d = {1: r}
>>> del r
>>> gc.get_referrers(d[1])
[{1: <__main__.A instance at 0x0000000002663708>}]

See also issue 4688 .

This is a change in how objects are tracked in Python 2.7; tuples and dictionaries containing only atomic types (including instances of object() ), which would never require cycle breaking, are not listed anymore.

See http://bugs.python.org/issue4688 ; this was implemented to avoid a performance issues with creating loads of tuples or dictionaries.

The work-around is to add an object to your dictionary that does need tracking:

>>> gc.is_tracked(d)
False
>>> class Foo(object): pass
...
>>> d['_'] = Foo()
>>> gc.is_tracked(d)
True
>>> d in gc.get_referrers(r)
True

Once tracked, a dictionary only goes back to being untracked after a gc collection cycle:

>>> del d['_']
>>> gc.is_tracked(d)
True
>>> d in gc.get_referrers(r)
True
>>> gc.collect()
0
>>> gc.is_tracked(d)
False
>>> d in gc.get_referrers(r)
False  

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM