Python's context managers are a very neat way of handling code that needs a teardown once you are done. Python objects have do have a destructor method (
One thing that is important, and that got me just now, is error handling. I made the mistake of ignoring all those 'junk' arguments (
So what happens now if you do
Of course, YOU know that this code will error out - you can't subtract strings. But if you run this code, it will FAIL SILENTLY. This is because I was careless and did not consider what happens if there is an error SOMEWHERE ELSE.
The proper way to do this, as a minimum, is to change the code to
Another note: defining both a
__del__) called right before the last instance of the object is about to be destroyed. You can do a teardown there. However there is a lot of fine print to the __del__ method. A cleaner way of doing tear-downs is through Python's context manager, manifested as the with keyword.class CrushMe:
def __init__(self):
self.f = open('test.txt', 'w')
def foo(self, a, b):
self.f.write(str(a - b))
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
self.f.close()
return True
with CrushMe() as c:
c.foo(2, 3)
One thing that is important, and that got me just now, is error handling. I made the mistake of ignoring all those 'junk' arguments (
exc_type, exc_val, exc_tb). I just skimmed the docs and what popped out is that you need to return True or False depending on whether there was an error. So I wrote my code to return False if there was a problem in saving the file (My actual code is a little more involved, but the same in spirit) and True otherwise.So what happens now if you do
with CrushMe() as c:
c.foo('2', '3')
Of course, YOU know that this code will error out - you can't subtract strings. But if you run this code, it will FAIL SILENTLY. This is because I was careless and did not consider what happens if there is an error SOMEWHERE ELSE.
The proper way to do this, as a minimum, is to change the code to
def __exit__(self, exc_type, exc_val, exc_tb):
self.f.close()
return True if exc_type is None else False
Another note: defining both a
__del__ method and an __exit__ method can lead to tricky situations. The following code, for, instance, calls the close method twice.class CrushMe:
def __init__(self):
self.f = open('test.txt', 'w')
def foo(self, a, b):
self.f.write(str(a - b))
def __del__(self):
self.close()
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
self.close()
return True if exc_type is None else False
def close(self):
print 'Called!'
self.f.close()
with CrushMe() as c:
c.foo(2, 3)
close gets called first by __exit__ when we exit the context and then when we exit the interpreter and the object is deleted.
Comments
Post a Comment