赞
踩
简单介绍下我认识contextlib的过程吧,觉得这个内置lib还挺有意思的。
1、
之前的我,只知道with会用来关闭文件,数据库资源,这很好。
只要实现了__enter__() 和 __exit__()这两个方法的类都可以轻松创建上下文管理器,就能使用with。
2、
我打开两个数据库的时候,都是
- with xxx as conn1:
- with yyy as conn2:
- code
真是蠢如老狗呀,其实可以:
- with xxx as conn1, yyy as conn2:
- code
3、
总感觉离开了with block,语句体的资源(文件啊,数据库连接啊,网络请求呀)就会自动关闭。
可是有一天我看到contextlib.closing()。 一脸懵逼,有了with还要这个干嘛,这是我内心真实OS。。
- from contextlib import closing
- from urllib2 import urlopen
-
- with closing(urlopen('http://www.python.org';)) as page:
- for line in page:
- print(line)
先来否定我的想法,凡用with就万事大吉,自动帮我关闭。
- class Door(object):
- def open(self):
- print 'Door is opened'
-
- def close(self):
- print 'Door is closed'
-
- with Door() as d:
- d.open()
结果:
- # 报错:
- Traceback (most recent call last):
- File "1.py", line 38, in <module>
- with Door() as d:
- AttributeError: __exit__
果然呢,因为with语句体执行之前运行__enter__方法,在with语句体执行完后运行__exit__方法。
如果一个类如Door连这两个方法都没有,是没资格使用with的。
4、
好吧,正式认识下contextlib:https://docs.python.org/dev/library/contextlib.html
有些类,并没有上述的两个方法,但是有close(),能不能在不加代码的情况下,使用with呢?
可以:
- class Door(object):
- def open(self):
- print 'Door is opened'
-
- def close(self):
- print 'Door is closed'
-
- with contextlib.closing(Door()) as door:
- door.open()
结果:
- Door is opened
- Door is closed
contextlib.closing(xxx),原理如下:
- class closing(object):
- """Context to automatically close something at the end of a block.
- Code like this:
- with closing(<module>.open(<arguments>)) as f:
- <block>
- is equivalent to this:
- f = <module>.open(<arguments>)
- try:
- <block>
- finally:
- f.close()
- """
- def __init__(self, thing):
- self.thing = thing
- def __enter__(self):
- return self.thing
- def __exit__(self, *exc_info):
- self.thing.close()
这个contextlib.closing()会帮它加上__enter__()和__exit__(),使其满足with的条件。
5、
是不是只有类才能享受with的便利呀? 我单单一个方法行不行?
行!既然认识了contextlib.closing(),必须认识下contextlib.contextmanager
这是一个装饰器,可以让一个func()变成一个满足with条件的类实例…
!!!这个func()必须是生成器…
yield前半段用来表示__enter__()
yield后半段用来表示__exit__()
- from contextlib import contextmanager
-
- @contextmanager
- def tag(name):
- print("<%s>" % name)
- yield
- print("</%s>" % name)
-
- with tag("h1"):
- print 'hello world!'
结果:
- <h1>
- hello world!
- </h1>
Wow,这个还真的挺酷的,以后可以用这个contextmanager来实现一些装饰器才能做的事,
比如给一段代码加时间cost计算:
装饰器版本:
- import time
- def wrapper(func):
- def new_func(*args, **kwargs):
- t1 = time.time()
- ret = func(*args, **kwargs)
- t2 = time.time()
- print 'cost time=', (t2-t1)
- return ret
- return new_func
-
- @wrapper
- def hello(a,b):
- time.sleep(1)
- print 'a + b = ', a+b
-
- hello(100,200)
结果:
- a + b = 300
- cost time= 1.00243401527
contextmanger版本:
- from contextlib import contextmanager
-
- @contextmanager
- def cost_time():
- t1 = time.time()
- yield
- t2 = time.time()
- print 'cost time=',t2-t1
-
- with cost_time():
- time.sleep(1)
- a = 100
- b = 200
- print 'a + b = ', a + b
结果:
- a + b = 300
- cost time= 1.00032901764
当然还是用装饰器方便美观点啦~
这是contextmanager原理:
源码:
- class GeneratorContextManager(object):
- """Helper for @contextmanager decorator."""
-
- def __init__(self, gen):
- self.gen = gen
-
- def __enter__(self):
- try:
- return self.gen.next()
- except StopIteration:
- raise RuntimeError("generator didn't yield")
-
- def __exit__(self, type, value, traceback):
- if type is None:
- try:
- self.gen.next()
- except StopIteration:
- return
- else:
- raise RuntimeError("generator didn't stop")
- else:
- if value is None:
- # Need to force instantiation so we can reliably
- # tell if we get the same exception back
- value = type()
- try:
- self.gen.throw(type, value, traceback)
- raise RuntimeError("generator didn't stop after throw()")
- except StopIteration, exc:
- return exc is not value
- except:
- if sys.exc_info()[1] is not value:
- raise
-
-
- def contextmanager(func):
- @wraps(func)
- def helper(*args, **kwds):
- return GeneratorContextManager(func(*args, **kwds))
- return helper
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。