7

I'm a noob with Python, but I've written an auto-close function like this..

@contextmanager
def AutoClose(obj):
    try:
        yield obj
    finally:
        obj.Close()

I have three classes that have a Close() method that this function can be used with. Is this the most Pythonic solution? Should I be doing something in the classes themselves instead?

bluedog
  • 935
  • 1
  • 8
  • 24
  • That seems good. You can also use a destructor in your class. – akiniwa Aug 12 '13 at 06:40
  • 1
    I would avoid using the destructor (i.e. `__del__`). It's really hard to reason about when/if it gets called. See the accepted answer here: http://stackoverflow.com/questions/1935153/del-method-being-called-in-python-when-it-is-not-expected – tom Aug 12 '13 at 06:41

2 Answers2

15

Most pythonic solution is to define methods __enter__ and __exit__ methods in your class:

class Foo(object):
     def __init__(self, filename):
         self.filename = filename

     def __enter__(self):
         self.fd = open(self.filename)

     def __exit__(self, exc_type, exc_value, traceback):
         self.fd.close()

And using:

with Foo('/path/to/file') as foo:
    # do something with foo

Methods __enter__ and __exit__ will be implicitly called when entering and leaving blocks with. Also note that __exit__ allows you to catch exception which raised inside the block with.

Function contextlib.closing is typically used for those classes that do not explicitly define the methods __enter__ and __exit__ (but have a method close). If you define your own classes, much better way is to define these methods.

defuz
  • 26,721
  • 10
  • 38
  • 60
  • +1 - This is an equally good answer IMO. I have chosen @tom s answer because it is the solution I will use on this occasion. The three particular classes all have a Close() method and have no requirement for \_\_enter\_\_, so the single auto_close function can be used for all three classes without touching the classes themselves. – bluedog Aug 12 '13 at 22:25
  • 1
    your `__enter__` neglects to return something useful e.g. `self`. It took me ages to track down the problem. – ctrl-alt-delor Jun 05 '22 at 19:19
10

What you're doing looks totally fine and Pythonic. Although, the contextlib standard library already has something similar, but you'll have to rename your Close methods to close.

import contextlib
with contextlib.closing(thing):
    print thing

I would recommend using this instead. After all, the recommended naming convention for Python methods is all_lowercase_with_underscores.

tom
  • 18,953
  • 4
  • 35
  • 35
  • Note that 'bluedog' may also have to rename his (or her) .Close() method to .close() order to use contextlib.closing() (unless I'm mistaken). – Jim Dennis Aug 12 '13 at 07:14
  • I have to keep the Close() method with current case, but I may add an alias called close() so that the standard contextlib.closing function can be used. – bluedog Aug 12 '13 at 22:29