不得不说派森大法好!
最近读代码发现了with这个好东西,通常可用于替换try,或者用在锁区域,出了这段代码锁自然释放这点跟java的synchronized关键词有点像,看个代码:
_pool_create_sem = threading.Lock() def get_connection_pool(conf, connection_cls): with _pool_create_sem: # Make sure only one thread tries to create the connection pool. if not connection_cls.pool: connection_cls.pool = ConnectionPool(conf, connection_cls) return connection_cls.pool
在进入with后面的语句块内时,必定获得了锁,到达return语句时必然已经释放了锁(即使有异常抛出也会释放),再也不用担心忘记解锁了。
派森大法中把像_pool_create_sem这样的对象称作 With Statement Context Manager,如果想在with中使用自己定义类的实例对象,就要在类中实现下面两个方法:
- object.__enter__(self)
-
Enter the runtime context related to this object. The with statement will bind this method’s return value to the target(s) specified in the as clause of the statement, if any.
- object.__exit__(self, exc_type, exc_value, traceback)
-
Exit the runtime context related to this object. The parameters describe the exception that caused the context to be exited. If the context was exited without an exception, all three arguments will be None.
If an exception is supplied, and the method wishes to suppress the exception (i.e., prevent it from being propagated), it should return a true value. Otherwise, the exception will be processed normally upon exit from this method.
Note that __exit__() methods should not reraise the passed-in exception; this is the caller’s responsibility.
__enter__在进入with后面的代码段之前执行,可以进行一些准备工作,比如把锁给锁上。__exit__在with后面的代码段退出时执行,可以进行一些资源的清理工作。尝试一发:
>>> class FakeLock(object): ... def __enter__(self): ... print 'locked' ... def __exit__(self, exc_type, exc_value, traceback): ... print 'unlocked' ... >>> with FakeLock(): ... print "I'm safe" ... locked I'm safe unlocked
可以看到__exit__方法还可以有许多参数都是关于with语句块内发生异常时的情况,一样我们可以把异常处理放在__exit__函数中,不必在写冗长的try语句了。
另外with语句可以可以有一个as关键词用于声明一个with使用的对象的别名,这样我们就能在with语句块内使用它了,经常出现在基于连接,读写文件的操作上,贴代码:
>>> with open('e:/static.txt') as f: ... f.readline() ... 'auto eth0 '
这样就不用手工去close file了,不过这里它似乎没有对异常进行处理会直接抛出。下面来自己写一个,这时__enter__方法需要有一个返回值,这个返回值就会赋给with as后面的变量
>>> class FakeConnection(object): ... def __enter__(self): ... print 'connection established!' ... return self ... def __exit__(self, ex_type, ex_value, traceback): ... if ex_type is not None: ... print 'execption detected:', ex_type, ex_value ... print 'connection disconnected!' ... return True ... def send(self): ... print 'msg send' ...
先定义一个假的连接对象,在__enter__中建立连接,然后返回自身。在__exit__中会记录抓到的异常(如果后面三个参数不为None),并断开连接,返回True表示异常以及被处理,不会再向上层调用者传递。运行一发:
>>> with FakeConnection() as conn: ... conn.send() ... ... connection established! msg send connection disconnected! >>> with FakeConnection() as conn: ... conn.send() ... conn.methodNotExist() ... connection established! msg send execption detected: <type 'exceptions.AttributeError'> 'FakeConnection' object has no attribute 'methodNotExist' connection disconnected!