Hacker News new | ask | show | jobs
by eternityforest 1010 days ago
Python handles all kinds of stuff with garbage collection.

The problem is that things like closing a socket are not just generic resources, a lot of the time nonmemory stuff has to be closed at a certain point in the program, for correctness, and you can't just let GC get to it whenever.

1 comments

I don’t think this is true. Context managers call special magic “dunder” methods on the instance (I don’t remember the specific ones), and I’m pretty sure those don’t get called during regular garbage collection of those instances. It’s been a few years since I was regularly writing python, so I might be wrong, but I don’t believe that context manager friendly instances are the same as Rust’s Drop trait, and I don’t think their cleanup code gets called during GC.
Python is a fun case of "all of the above" (or rather, a layering of styles once it turns out a previous one isn't workable).

Originally, they used pure reference counting GC, with finalizers used to clean up when freed. This was "fine", since RC is deterministic. Everything is freed when the last reference is deleted, nice and simple.

But reference counting can't detect reference cycles, so eventually they added a secondary tracing garbage collector to handle them. But tracing GC isn't deterministic anymore, so this also meant a shift to manual resource management.

That turned out to be embarrassing enough that context managers were eventually introduced to paper over it. But all four mechanisms still exist and "work" in the language today.

Are you saying that a finalizer is guaranteed to run when the last reference is deleted? So you could actually rely on them to handle the resources, as long as you are careful not to use reference cycles?
In CPython 2.7, yes. In CPython in general, I believe it's currently still the case, but I don't think it's guaranteed for future versions.

For Python in general, no. For example, as far as I know Jython reuses the JVM's GC (and its unreliable finalizers with it).

It's also easy to introduce accidental cycles. For one, a traceback includes a reference to every frame on the call stack, so storing that somewhere on the stack would create an unintentional cycle!

The tracebacks were a lot of what made me cut back on using weakrefs and trying to make things manage their resources automatically.

Now I use close() methods for anything that needs to be closed. If I mess up and there's some obscure bug, hopefully GC will fix it, but it seems too brittle and easy to make mistakes with to rely on.

Wrote Python professionally for years and didn’t know all of this. Thanks!
with: does in fact use different dunder methods, but __del__ allows one to do GC-based cleanup if one wishes.