with open("foo.txt") as fp: for line in fp: if line.startswith("ERROR"): print line fp.seek() # doesn't work, file handle is automatically closed by contextmanager
Python instructor for Marakana
simeon@marakana.com
@simeonfranklin
http://simeonfranklin.com/blog/
Everybody knows what with is about, right?
with open("foo.txt") as fp: for line in fp: if line.startswith("ERROR"): print line fp.seek() # doesn't work, file handle is automatically closed by contextmanager
This is a cool idea. It’s like decorators for blocks of code instead of functions!
Any time you have a before/after or do/undo kind of API. Especially if the after/undo part should happen no matter what!
Programmers always follow instructions, so you can just tell people to make sure to put the .after() call in a finally block, right?
For example:
define a class with an __enter__ and __exit__ method.
__enter__ is the before method and can return the value you catch with the as clause.
__exit__ is the after method and accepts three arguments (exception_type, exception_instance, traceback). It can return True to supress exceptions that occur in the with block.
Use the __init__ to catch arguments to your contextmanager (filename to open, etc)
But it takes several concepts packed into a tight space.
Phenomenal cosmic power!
For the purposes of this lightning talk, yield is a return that pauses the function instead of ending it.
Functions with yield in them aren’t really functions.
Think of them as constructors for generator objects.
>>> def do_two_things(): ... print "first thing" ... yield ... print "second thing" ... >>> gen = do_two_things() >>> gen <generator object do_two_things at 0x11078db90> >>> gen.next() first thing >>> gen.next() second thing Traceback (most recent call last): ... StopIteration
I don’t really have to explain this, do I?
try: print 5/0 # usually doing something stupid crashes my program finally: print "But this line always runs no matter what!"
If you don’t understand them… magic applied to functions.
>>> class Foo(object): ... @staticmethod ... def normal_function(): # notice no self! Magic! ... print "Usually leaving off self is a mistake!" ... >>> Foo.normal_function() Usually leaving off self is a mistake!
A decorator that can build the contextmanager for you!
Et. Voila!
>>> from contextlib import contextmanager >>> import os, glob >>> >>> @contextmanager ... def cd(path): ... try: ... cur = os.getcwd() ... os.chdir(os.path.expanduser(path)) ... yield # end the "before" stuff. Could yield a value ... finally: # use try/finally in case the block crashes ... os.chdir(cur) # this is the "after" stuff ... >>> with cd("~/work/talks"): ... print glob.glob("*txt") ['contextlib.txt']
questions? (I bet my 5 minutes is up!)
/
#