1

In the following code, the object named a is an attribute of itself, which creates a reference cycle.

class MyClass(object):
     pass

 a = MyClass()
 a.obj = a

If I were to then call del a, I supposedly would not have gotten rid of all the references to a, since the self-referencing nature of a is supposed to prevent it from having a non-zero reference count.

I'm not sure why it has to be the case that reference cycles prevent the reference count from going to 0. Could someone explain this to me, step-by-step?

8
  • 1
    a.obj is a reference to a. Any way you count that, the reference count isn't 0. Commented Apr 30, 2020 at 1:01
  • 1
    "If I were to then call del a, I supposedly would not have gotten rid of all the references " no, you wouldn't, because there still exists a reference to the object, namely, in it's .a attribute. If you did del a.obj; del a then there would be no more references to the object Commented Apr 30, 2020 at 1:03
  • 1
    This isn't specific to Python, all systems with reference counting could have such cycles and the same problem. It's a generic and very open-ended topic, with tons of descriptions and research material available with a little searching. Commented Apr 30, 2020 at 1:07
  • 2
    @Asker why do you think it would? del removes names, it doesn't affect objects at all. if I had a = object(); b = a; del a then b is still referring to the same object, and it hasn't been affected at all by del a (aside from it's internal reference count being lowered by 1) Commented Apr 30, 2020 at 1:11
  • 3
    del a just unassigns a variable. It doesn't do anything to the object the variable used to refer to. Commented Apr 30, 2020 at 1:11

1 Answer 1

7
class MyClass(object):
     pass

a = MyClass()
# for clarity, let's call this object "trinket"
# (to dissociate the object from the variable)
# one reference to trinket: variable a

a.obj = a
# two references to trinket: variable a, trinket.obj

del a
# one reference to trinket: trinket.obj
# (because del doesn't delete the object, just the variable)

Thus, reference counting garbage collector cannot dispose of this trinket. Fortunately, Python has another garbage collector, a generational garbage collector (unless you disable it, using gc.disable()). It runs periodically, and when it runs, it will dispose of our trinket, even though a dangling reference remains.

Sign up to request clarification or add additional context in comments.

5 Comments

Thanks for this answer. What if a.obj were replaced with b? Then it seems b would be a reference to trinket. It seems to me that del a then leave the reference b unchanged, and thus there would still be a reference to trinket afterwards. Yet, this is in spite of the fact that, with b in place of a.obj, the assignment would not create a reference cycle! What (if anything) is wrong with my logic?
@Asker well yes, but it wouldn't be a reference cycle. You'd simply just have another reference to trinket, namely, b... The same as a = object(); b = object(); del a
If you have a = MyClass(); b = a, then there are two references (variable a, variable b). There are no cycles; when both variables cease existing, the reference count will reach zero and the object will be disposed of. As long as you have either a or b in scope, the object is legitimately in use, and should not be disposed of.
The difference is that trinket.obj cannot simply disappear: variables have scope, but object attributes do not. If you make a local variable b in a function, at the end of the function the variable goes "poof" even if you don't explicitly delete it. But trinket.obj will stick on trinket as long as trinket exists, and (in turn) trinket is not allowed to stop existing as long as its .obj is refering to it (unless, as I said, the generational GC deals with it).
Thank you! All is clear now. Variables vanish (or rather, lose their bindings) outside of their scopes, yet objects must rely on either reference-counting- or generational-garbage collecting... and the former never happens if an attribute of an object references said object, since the attribute lives as long as the object lives, and vice versa in the case of reference-counting garbage collection. I would upvote this answer a million times if I could. You went straight to the heart of the matter!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.