0

If I have a class that wraps a resource, e.g., an sqlite database connection or a file, is there a way I can use the with statement to close the resource when my object goes out of scope or is gcollected?

To clarify what I mean, I want to avoid this:

class x:
    def __init__(self):
        # open resource

    def close(self): # or __del__, even worst
        # close resource

but make it in such a way that the resource is always freed as in

with open('foo') as f:
    # use resource
Vincenzo Pii
  • 18,961
  • 8
  • 39
  • 49

3 Answers3

3

You need to provide __enter__ and __exit__ methods. See PEP 343.

This PEP adds a new statement "with" to the Python language to make it possible to factor out standard uses of try/finally statements.

In this PEP, context managers provide __enter__() and __exit__() methods that are invoked on entry to and exit from the body of the with statement.

roippi
  • 25,533
  • 4
  • 48
  • 73
  • You don't actually need to do this if you take advantage of `contextlib`. `contextlib` will handle it for you. – user2357112 Aug 07 '13 at 19:35
  • 1
    @user2357112 that is appropriate if you are using an external API that does not provide a context manager. If you are _writing_ one, you should actually put it into the API rather than forcing callers to handle cleanup. – roippi Aug 07 '13 at 19:38
  • Ah, this is about writing the API, not using it. Providing the methods yourself makes sense, then. – user2357112 Aug 07 '13 at 19:42
1

Use contextlib.closing:

with contextlib.closing(thing) as thing:
    do_stuff_with(thing)
# Thing is closed now.
user2357112
  • 260,549
  • 28
  • 431
  • 505
  • I don't think this answers the question he's looking for. He wants the `thing` to be an attribute of `x` instances, and for `thing.close()` to be called when `x` is freed. So, there's no static scope he can put a `with` statement in. – abarnert Aug 07 '13 at 20:07
  • @abarnert: No, I'm pretty sure he wants to use a `with` statement. The question even says "is there a way I can use the `with` statement". Relying on object destruction to close resources is a bad idea in Python. (Since he's writing the API rather than dealing with an API that doesn't provide context manager methods, roippi's answer is more appropriate, so I've upvoted that one.) – user2357112 Aug 07 '13 at 20:09
  • Yes, he wants to know if there's a way to use a `with` statement inside his class. Since the lifetime of his objects is not bound to any static scope, the answer is no, he can't do that. It's a reasonable question—in some languages, like C++, you not only can, but usually _should_ make the object manage its own resources. But in Python, you can't, and shouldn't; the user of the object needs to explicitly `close` it; the only thing the object can do is make it easier on them. – abarnert Aug 07 '13 at 20:14
  • @abarnert: I think we're interpreting the question differently. I see the part about "use the `with` statement" and the example code with an `open`ed file and think that he wants to do `with x() as thing: do_stuff_with(thing)`. I'm guessing you see the part about "when my object goes out of scope or is gcollected" and think that he's looking for a destructor. I see that part as just a slight misunderstanding of the `with` statement. I'm pretty sure he wants to be able to use his object in a `with` statement, not use a `with` statement in his object. – user2357112 Aug 07 '13 at 20:25
  • The OP even says that a `__del__` method would be "even worst [sic]" than a `close` method, which he's already trying to avoid. – user2357112 Aug 07 '13 at 20:28
  • I think that "slight misunderstanding" is the key to the whole question the OP is asking. If you don't clear that up, no answer is going to be useful to him. If you do, the answers become obvious. – abarnert Aug 07 '13 at 21:16
  • I think abarnert spot me in that "in some languages, like C++, you not only can, but usually should make the object manage its own resources". I am a C++ guy that wants to be pythonic :D. Concerning the question, the fact that I didn't know if there was a way to use `with` directly on the `thing` is the reason I asked it! Apparently that is not possible and we need to use `with` on `x` by making it a context manager :)! This is one of the best discussions I've seen here around! Thank you guys. – Vincenzo Pii Aug 08 '13 at 14:59
1

You can always put any cleanup code you need into a class's __del__ method:

class x:
    def __init__(self):
        self.thing = get_thing()

    def __del__(self):
        self.thing.close()

But you shouldn't.

This is a bad idea, for a few reasons. If you're using CPython, having custom __del__ methods means the GC can't break reference cycles. If you're using most other Python implementations, __del__ methods aren't called at predictable times.

This is why you usually put cleanup in explicit close methods. That's the best you can do within the class itself. It's always up to the user of your class to make sure the close method gets called, not the class itself.

So, there's no way you can use a with statement, or anything equivalent, inside your class. But you can make it easier for users of your class to use a with statement, by making your class into a context manager, as described in roippi's answer, or just by suggesting they use contextlib.closing in your documentation.

abarnert
  • 354,177
  • 51
  • 601
  • 671