Other resources

The with-statement construct can be used with any type of object which implements the context manager protocol. We're not going to show you how to implement a context-manager is this book – for that you'll need to refer to The Python Journeyman – but we will show you a simple way to make your own classes usable in a with statement. Put this code into a the module fridge.py:

# fridge.py

"""Demonstrate raiding a refrigerator."""

class RefrigeratorRaider:
"""Raid a refrigerator."""

def open(self):
print("Open fridge door.")

def take(self, food):
print("Finding {}...".format(food))
if food == 'deep fried pizza':
raise RuntimeError("Health warning!")
print("Taking {}".format(food))

def close(self):
print("Close fridge door.")

def raid(food):
r = RefrigeratorRaider()
r.open()
r.take(food)
r.close()

We'll import raid() into the REPL and go on the rampage:

>>> from fridge import raid
>>> raid("bacon")
Open fridge door.
Finding bacon...
Taking bacon
Close fridge door.

Importantly, we remembered to close the door, so the food will be preserved until our next raid. Let's try another raid for something slightly less healthy:

>>> raid("deep fried pizza")
Open fridge door.
Finding deep fried pizza...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "./fridge.py", line 23, in raid
r.take(food)
File "./fridge.py", line 14, in take
raise RuntimeError("Health warning!")
RuntimeError: Health warning!

This time, we were interrupted by the health warning and didn't get around to closing the door. We can fix that by using a function called closing() in the Python Standard Library contextlib module. After importing the function we wrap our RefrigeratorRaider constructor call in a call to closing(). This wraps our object in a context manager that always calls the close() method on the wrapped object before exiting. We use this
object to initialise a with-block:

"""Demonstrate raiding a refrigerator."""

from contextlib import closing

class RefrigeratorRaider:
"""Raid a refrigerator."""

def open(self):
print("Open fridge door.")

def take(self, food):
print("Finding {}...".format(food))
if food == 'deep fried pizza':
raise RuntimeError("Health warning!")
print("Taking {}".format(food))

def close(self):
print("Close fridge door.")

def raid(food):
with closing(RefrigeratorRaider()) as r:
r.open()
r.take(food)
r.close()

Now when we execute a raid:

>>> raid("spam")
Open fridge door.
Finding spam...
Taking spam
Close fridge door.
Close fridge door.

We see that our explicit call to close() is unnecessary, so let's fix that up:

def raid(food):
with closing(RefrigeratorRaider()) as r:
r.open()
r.take(food)

A more sophisticated implementation would check that the door was already closed and ignore other requests.

So does it work? Let's try eating some deep fried pizza once more:

>>> raid("deep fried pizza")
Open fridge door.
Finding deep fried pizza...
Close fridge door.
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "./fridge.py", line 23, in raid
r.take(food)
File "./fridge.py", line 14, in take
raise RuntimeError("Health warning!")
RuntimeError: Health warning!

This time, even though the health warning was triggered, the door was still closed for us by the context manager.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset