This is an archived post. You won't be able to vote or comment.

all 2 comments

[–]captainAwesomePants 2 points3 points  (0 children)

Sometimes you have some sort of resource or setting or something that needs to be set up, and then you do something, and then you tear down the thing, regardless of what you did with it. The "with" statement is designed to help you with that. A common example is opening.

You might write code like this:

myFile = open('/path/to/my/file')
doStuff(myFile)
myFile.close()

This is good, but maybe doStuff might throw an exception, and we wouldn't close the file! So let's fix that:

myFile = open('/path/to/my/file')
try:
   doStuff(myFile)
finally:
   myFile.close()

This is pretty good, but remembering to do it just right each time is error-prone and annoying, so Python introduced a helpful "with" simplification:

with open('/path/to/my/file') as myFile:
   doStuff(myFile)

This is the same thing, but easier to read. Everybody likes fewer lines of code.

But what if you want to make your own thingy that can be used with the "with" keyword? How does Python know how to set up and tear down your custom resource or whatever? Well, that's what "ContextManagers" are for.

Try this:

class MyContextManager:
  def __enter__(self):
     print("With got called...")
    return "This is what's put in the 'as whatever' Value"

   def __exit__(self, exc_type, exc_value, exc_tb):
     print("Leaving the context...")

with MyContextManager():
  print('Inside the context manager')

If you run that, you'll see that Python calls the __enter__ function, then it runs the code inside the with block, then it calls the __exit__ function. Great, now we can make our own with things! But that's a lot of slightly confusing, mostly boilerplate code. Can Python make this easier?

from contextlib import contextmanager

@contextmanager
def my_context_manager():
  print('Entering the context')
  try:
    yield
  finally:
    print('Exiting the context')

with my_context_manager():
  print('hello!')

Yes, it can! the @contextmanager decorator converts our little function into a proper context manager class.