作用:处理其他函数的函数
Python版本:2.5及以后版本
functools模块提供了一些工具来调整或扩展函数和其他可回调对象,而不必完全重写.
functools模块提供的主要工具是partial类,它可以用来"包装"一个有默认参数的可回调对象.得到的对象本身是可回调的,可以看作就像是原来的函数.它与原函数的参数完全相同,调用时也可以提供额外的位置或命名参数.可以使用partial而不是lambda为函数提供默认参数,有些参数可以不指定.
partial对象实际上可以理解为"C++的仿函数",JavaScript的curry化,和Python本身的装饰器功能类似,但更强大:
import functools def myfunc(a, b = 2): print ' called myfunc with:', (a, b) return def show_details(name, f, is_partial = False): print '%s:' % name print ' object:', f if not is_partial: print ' __name__:', f.__name__ if is_partial: print ' func:', f.func print ' args:', f.args print ' keywords:', f.keywords return show_details('myfunc', myfunc) myfunc('a', 3) print #set a different default value for 'b', but require #the caller to provide 'a' p1 = functools.partial(myfunc, b = 4) show_details('partial with named default', p1, True) p1('passing a') p1('override b', b = 5) print #set default value for both 'a' and 'b' p2 = functools.partial(myfunc, 'default a', b = 99) show_details('partial with defaults', p2, True) p2() p2(b = 'override b') print print 'Insufficient argument:' p1()解释器显示如下:
>>> myfunc: object: <function myfunc at 0x00000000021B49E8> __name__: myfunc called myfunc with: ('a', 3) partial with named default: object: <functools.partial object at 0x00000000027AD638> func: <function myfunc at 0x00000000021B49E8> args: () keywords: {'b': 4} called myfunc with: ('passing a', 4) called myfunc with: ('override b', 5) partial with defaults: object: <functools.partial object at 0x00000000027AD688> func: <function myfunc at 0x00000000021B49E8> args: ('default a',) keywords: {'b': 99} called myfunc with: ('default a', 99) called myfunc with: ('default a', 'override b') Insufficient argument: Traceback (most recent call last): File "C:\Python27\test.py", line 38, in <module> p1() TypeError: myfunc() takes at least 1 argument (1 given)
而一个简单的装饰器实例如下:
def show(num): def oneFunc(func): def childFunc(name): num.append(name) print("num is") print(num) return childFunc return oneFunc @show([1, 2, 3]) def func(name): return name func("hello")解释器显示如下:
>>> num is [1, 2, 3, 'hello']
默认情况下,partial对象没有__name__或__doc__属性.如果没有这些属性,修饰的函数将更难调试.使用update_wrapper()可以从原函数将属性复制或添加到partial对象.
import functools def myfunc(a, b = 2): """Docstring for myfunc().""" print ' called myfunc with:', (a, b) return def show_details(name, f): """Show details of a callable object.""" print '%s:' % name print ' object:', f print ' __name__:', try: print f.__name__ except AttributeError: print '(no __name__)' print ' __doc__', repr(f.__doc__) print return show_details('myfunc', myfunc) p1 = functools.partial(myfunc, b = 4) show_details('raw wrapper', p1) print 'Updating wrapper:' print ' assign:', functools.WRAPPER_ASSIGNMENTS print ' update:', functools.WRAPPER_UPDATES print functools.update_wrapper(p1, myfunc) show_details('updated wrapper', p1)解释器显示如下:
>>> myfunc: object: <function myfunc at 0x00000000021449E8> __name__: myfunc __doc__ 'Docstring for myfunc().' raw wrapper: object: <functools.partial object at 0x00000000028ED638> __name__: (no __name__) __doc__ 'partial(func, *args, **keywords) - new function with partial application\n of the given arguments and keywords.\n' Updating wrapper: assign: ('__module__', '__name__', '__doc__') update: ('__dict__',) updated wrapper: object: <functools.partial object at 0x00000000028ED638> __name__: myfunc __doc__ 'Docstring for myfunc().'
Partial适用于任何可回调对象,而不只是单独的函数
import functools class MyClass(object): """Demonstration class for functools""" def method1(self, a, b = 2): """Docstring for method1().""" print ' called method1 with:', (self, a, b) return def method2(self, c, d = 5): """Docstring for method2().""" print ' called method2 with:', (self, c, d) return wrapped_method2 = functools.partial(method2, 'wrapped c') functools.update_wrapper(wrapped_method2, method2) def __call__(self, e, f = 6): """Docstring for MyClass.__call__""" print ' called object with:', (self, e, f) return def show_details(name, f): """Show details of a callable object.""" print '%s:' % name print ' object:', f print ' __name__:', try: print f.__name__ except AttributeError: print '(no __name__)' print ' __doc__', repr(f.__doc__) return o = MyClass() #由类对象来创建实例,调用其方法method1 show_details('method1 straight', o.method1) o.method1('no default for a', b = 3) print p1 = functools.partial(o.method1, b = 4) functools.update_wrapper(p1, o.method1) show_details('method1 wrapper', p1) p1('a goes here') print show_details('method2', o.method2) o.method2('no default for c', d = 6) print #wrapped_method2在内部已经实现了update_wrapper操作 show_details('wrapped method2', o.wrapped_method2) o.wrapped_method2('no default for c', d = 6) print #由类名来创建实例 show_details('instance', o) o('no default for e') print p2 = functools.partial(o, f = 7) show_details('instance wrapper', p2) p2('e goes here')解释器显示如下:
>>> method1 straight: object: <bound method MyClass.method1 of <__main__.MyClass object at 0x00000000028809E8>> __name__: method1 __doc__ 'Docstring for method1().' called method1 with: (<__main__.MyClass object at 0x00000000028809E8>, 'no default for a', 3) method1 wrapper: object: <functools.partial object at 0x000000000287D728> __name__: method1 __doc__ 'Docstring for method1().' called method1 with: (<__main__.MyClass object at 0x00000000028809E8>, 'a goes here', 4) method2: object: <bound method MyClass.method2 of <__main__.MyClass object at 0x00000000028809E8>> __name__: method2 __doc__ 'Docstring for method2().' called method2 with: (<__main__.MyClass object at 0x00000000028809E8>, 'no default for c', 6) wrapped method2: object: <functools.partial object at 0x000000000287D688> __name__: method2 __doc__ 'Docstring for method2().' called method2 with: ('wrapped c', 'no default for c', 6) instance: object: <__main__.MyClass object at 0x00000000028809E8> __name__: (no __name__) __doc__ 'Demonstration class for functools' called object with: (<__main__.MyClass object at 0x00000000028809E8>, 'no default for e', 6) instance wrapper: object: <functools.partial object at 0x000000000287D778> __name__: (no __name__) __doc__ 'partial(func, *args, **keywords) - new function with partial application\n of the given arguments and keywords.\n' called object with: (<__main__.MyClass object at 0x00000000028809E8>, 'e goes here', 7)
在修饰符中使用时,更新包装的可回调对象的属性尤其有用,因为变换后的函数最后会得到原'裸'函数的属性.
import functools def show_details(name, f): """Show details of a callable object.""" print '%s:' % name print ' object:', f print ' __name__:', try: print f.__name__ except AttributeError: print '(no __name__)' print ' __doc__', repr(f.__doc__) print return def simple_decorator(f): #装饰器起作用:这里decorated实际上等价于wraps(decorated)函数 @functools.wraps(f) def decorated(a = 'decorated defaults', b = 1): print ' decorated:', (a, b) print ' ', f(a, b = b) return return decorated def myfunc(a, b = 2): """myfunc() is not complicated""" print ' myfunc:', (a, b) return #the raw function show_details('myfunc', myfunc) myfunc('unwrapped, default b') myfunc('unwrapped, passing b', 3) print #wrap explicitly wrapped_myfunc = simple_decorator(myfunc) show_details('wrapped_myfunc', wrapped_myfunc) wrapped_myfunc() wrapped_myfunc('args to wrapped', 4) print #wrap with decorator syntax @simple_decorator def decorated_myfunc(a, b): """decorated_myfunc function""" myfunc(a, b) return show_details('decorated_myfunc', decorated_myfunc) decorated_myfunc() decorated_myfunc('args to decorated', 4)
解释器显示如下:
>>> myfunc: object: <function myfunc at 0x00000000028427B8> __name__: myfunc __doc__ 'myfunc() is not complicated' myfunc: ('unwrapped, default b', 2) myfunc: ('unwrapped, passing b', 3) wrapped_myfunc: object: <function myfunc at 0x0000000002842828> __name__: myfunc __doc__ 'myfunc() is not complicated' decorated: ('decorated defaults', 1) myfunc: ('decorated defaults', 1) decorated: ('args to wrapped', 4) myfunc: ('args to wrapped', 4) decorated_myfunc: object: <function decorated_myfunc at 0x0000000002842908> __name__: decorated_myfunc __doc__ 'decorated_myfunc function' decorated: ('decorated defaults', 1) myfunc: ('decorated defaults', 1) decorated: ('args to decorated', 4) myfunc: ('args to decorated', 4)备注:
1. functools提供了一个修饰符wraps(),它会对所修饰的函数应用update_wrapper().
2. 需要深入理解装饰器,才能理解以上代码.
设计富比较API是为了支持涉及复杂比较的类,从而以最高效的方式实现各个测试:
import functools import inspect from pprint import pprint @functools.total_ordering class MyObject(object): def __init__(self, val): self.val = val def __eq__(self, other): print ' testing __eq__(%s, %s)' % (self.val, other.val) return self.val == other.val def __gt__(self, other): print ' testing __gt__(%s, %s)' % (self.val, other.val) return self.val > other.val print 'Methods:\n' pprint(inspect.getmembers(MyObject, inspect.ismethod)) a = MyObject(1) b = MyObject(2) print '\nComparisons:' for expr in ['a < b', 'a <= b', 'a == b', 'a >= b', 'a > b']: print '\n%-6s:' % expr result = eval(expr) print ' result of %s: %s' % (expr, result)解释器显示如下:
>>> Methods: [('__eq__', <unbound method MyObject.__eq__>), ('__ge__', <unbound method MyObject.__ge__>), ('__gt__', <unbound method MyObject.__gt__>), ('__init__', <unbound method MyObject.__init__>), ('__le__', <unbound method MyObject.__le__>), ('__lt__', <unbound method MyObject.__lt__>)] Comparisons: a < b : testing __gt__(1, 2) testing __eq__(1, 2) result of a < b: True a <= b: testing __gt__(1, 2) result of a <= b: True a == b: testing __eq__(1, 2) result of a == b: False a >= b: testing __gt__(1, 2) testing __eq__(1, 2) result of a >= b: False a > b : testing __gt__(1, 2) result of a > b: False
由于在Python3中不用cmp,所以我们需要使用cmp_to_key对原cmp函数进行转换:
import functools class MyObject(object): def __init__(self, val): self.val = val def __str__(self): return 'MyObject(%s)' % self.val def compare_obj(a, b): """old-style comparison function.""" print 'comparing %s and %s' % (a, b) return cmp(a.val, b.val) #Make a key function using cmp_to_key() get_key = functools.cmp_to_key(compare_obj) def get_key_wrapper(o): """Wrapper function for get_key to allow for print statements""" new_key = get_key(o) print 'key_wrapper(%s) -> %s' % (o, new_key) return new_key objs = [MyObject(x) for x in range(5, 0, -1)] for o in sorted(objs, key=get_key_wrapper): print o解释器显示如下:
>>> key_wrapper(MyObject(5)) -> <functools.K object at 0x0000000002564798> key_wrapper(MyObject(4)) -> <functools.K object at 0x00000000027CF198> key_wrapper(MyObject(3)) -> <functools.K object at 0x00000000027CFA08> key_wrapper(MyObject(2)) -> <functools.K object at 0x00000000027CF1F8> key_wrapper(MyObject(1)) -> <functools.K object at 0x00000000027CF258> comparing MyObject(4) and MyObject(5) comparing MyObject(3) and MyObject(4) comparing MyObject(2) and MyObject(3) comparing MyObject(1) and MyObject(2) MyObject(1) MyObject(2) MyObject(3) MyObject(4) MyObject(5)
作用:itertools模块包含一组函数用于处理序列数据集
Python版本:2.3及以后版本
与使用列表的代码相比,基于迭代器的算法可以提供更好的内存使用特性.在真正需要数据之前,并不从迭代器生成数据,由于这个原因,不需要将所有数据都同时存储在内存中.这种"懒"处理模型可以减少内存使用,相应的还可以减少交换以及大数据集的其他副作用,从而改善性能.
chain函数取多个迭代器作为参数,最后返回一个迭代器,它能生成所有输入迭代器的内容,就好像这些内容来自一个迭代器一样.
利用chain(),可以轻松的处理多个序列而不必构造一个大的列表.
>>> from itertools import * >>> for i in chain([1, 2, 3], ['a', 'b', 'c']): print i, 1 2 3 a b cizip()返回一个迭代器,它会把多个迭代器的元素结合到一个元组中.类似于zip(),只不过它返回一个迭代器而不是一个列表:
>>> zip([1, 2, 3], ['a', 'b', 'c']) [(1, 'a'), (2, 'b'), (3, 'c')] >>> list(izip([1, 2, 3], ['a', 'b', 'c'])) [(1, 'a'), (2, 'b'), (3, 'c')]islice()函数返回一个迭代器,它按索引返回由输入迭代器所选的元素.它和slice()函数功能类似,只是返回迭代器.
>>> from itertools import * >>> count() count(0) >>> list(islice(count(), 5)) [0, 1, 2, 3, 4] >>> list(islice(count(), 5, 10)) [5, 6, 7, 8, 9] >>> list(islice(count(), 0, 100, 10)) [0, 10, 20, 30, 40, 50, 60, 70, 80, 90]tee()函数根据一个原输入迭代器返回多个独立的迭代器(默认为两个).
>>> from itertools import * >>> r = islice(count(), 5) >>> i1, i2 = tee(r) >>> list(i1) [0, 1, 2, 3, 4] >>> list(i2) [0, 1, 2, 3, 4]tee()返回的迭代器可以用来为将并行处理的多个算法提供相同的数据集.但是tee()创建的新迭代器共享器输入迭代器,所以一旦创建了新迭代器,就不应再使用原迭代器.
from itertools import * r = islice(count(), 5) i1, i2 = tee(r) print 'r:' for i in r: print i, if i > 1: break print print 'i1:', list(i1) print 'i2:', list(i2) print id(i1),id(i2),id(r)解释器显示如下:
>>> r: 0 1 2 i1: [3, 4] i2: [3, 4] 42345288 42345160 43046456
imap()函数会返回一个迭代器,它对输入迭代器中的值调用一个函数并返回结果.imap()类似于map(),只不过只要有某个输入迭代器中的元素全部用完,imap()函数就会停止(而不是插入None值来完成利用所有输入).
from itertools import * for i in imap(lambda x: 2 * x, range(5)): print i, print for i in imap(lambda x, y: (x, y, x * y), range(5), range(5, 10)): print '%d * %d = %d' % i解释器显示如下:
>>> 0 2 4 6 8 0 * 5 = 0 1 * 6 = 6 2 * 7 = 14 3 * 8 = 24 4 * 9 = 36starmap()函数类似于imap(),不过并不是由多个迭代器构建一个tuple,它使用*语法分解一个迭代器中的元素作为映射函数的参数:
from itertools import * values = [(0, 5), (1, 6), (2, 7), (3, 8), (4, 9)] for i in starmap(lambda x, y: (x, y, x * y), values): print '%d * %d = %d' % i解释器显示如下:
>>> 0 * 5 = 0 1 * 6 = 6 2 * 7 = 14 3 * 8 = 24 4 * 9 = 36
count()函数返回一个迭代器,能否无限的生成连续整数.第一个数可以作为参数传入(默认为0).这里没有上限参数:
from itertools import * for i in izip(count(1), ['a', 'b', 'c']): print i,解释器显示如下:
>>> (1, 'a') (2, 'b') (3, 'c')而cycle()函数返回一个迭代器,它会无限的重复给定参数的内容.由于必须记住输入迭代器的全部内容,因此如果这个迭代器很长,可能会消费大量内存:
from itertools import * for i, item in izip(range(7), cycle(['a', 'b', 'c'])): print (i, item)解释器显示如下:
>>> (0, 'a') (1, 'b') (2, 'c') (3, 'a') (4, 'b') (5, 'c') (6, 'a')而repeat()函数返回一个迭代器,每次访问时会生成相同的值:
>>> from itertools import * >>> list(repeat('over-and-over', 5)) ['over-and-over', 'over-and-over', 'over-and-over', 'over-and-over', 'over-and-over']我们可以把repeat和imap等结合起来使用:
>>> list(imap(lambda x, y: (x, y, x * y), repeat(2), range(5))) [(2, 0, 0), (2, 1, 2), (2, 2, 4), (2, 3, 6), (2, 4, 8)]
dropwhile()函数返回一个迭代器,它会生成输入迭代器中条件第一次为false之后的元素:
from itertools import * def should_drop(x): print 'Testing:', x return (x < 1) for i in dropwhile(should_drop, [-1, 0, 1, 2, -2]): print 'Yielding:', i
解释器显示如下:
>>> Testing: -1 Testing: 0 Testing: 1 Yielding: 1 Yielding: 2 Yielding: -2而takewhile()返回一个迭代器,这个迭代器将返回输入迭代器中保证测试条件为true的元素:
from itertools import * def should_take(x): print 'Testing:', x return (x < 2) for i in takewhile(should_take, [-1, 0, 1, 2, -2]): print 'Yielding:', i解释器显示如下:
>>> Testing: -1 Yielding: -1 Testing: 0 Yielding: 0 Testing: 1 Yielding: 1 Testing: 2ifilter和filter类似,但是返回的是迭代器:
>>> list(ifilter(lambda x: x < 1, [-1, 0, 1, 2, -2])) [-1, 0, -2]而ifilterfalse()顾名思义,返回为false的元素:
>>> list(ifilterfalse(lambda x: x < 1, [-1, 0, 1, 2, -2])) [1, 2]
groupby()函数返回一个迭代器,它会生成一个按一个公共键组织的值集.
from itertools import * import operator import pprint class Point: def __init__(self, x, y): self.x = x self.y = y def __repr__(self): return '(%s, %s)' % (self.x, self.y) def __cmp__(self, other): return cmp((self.x, self.y), (other.x, other.y)) data = list(imap(Point, cycle(islice(count(), 3)), islice(count(), 7),)) print 'Data:' pprint.pprint(data, width = 69) print print 'Grouped, unsorted:' for k, g in groupby(data, operator.attrgetter('x')): print k, list(g) print data.sort() print 'Sorted:' pprint.pprint(data, width = 69) print print 'Grouped, sorted:' for k, g in groupby(data, operator.attrgetter('x')): print k, list(g) print解释器显示如下:
>>> Data: [(0, 0), (1, 1), (2, 2), (0, 3), (1, 4), (2, 5), (0, 6)] Grouped, unsorted: 0 [(0, 0)] 1 [(1, 1)] 2 [(2, 2)] 0 [(0, 3)] 1 [(1, 4)] 2 [(2, 5)] 0 [(0, 6)] Sorted: [(0, 0), (0, 3), (0, 6), (1, 1), (1, 4), (2, 2), (2, 5)] Grouped, sorted: 0 [(0, 0), (0, 3), (0, 6)] 1 [(1, 1), (1, 4)] 2 [(2, 2), (2, 5)]
作用:内置操作符的函数接口
Python版本:1.4及以后版本
>>> a = -1 >>> b = 5 >>> from operator import * >>> not_(a) False >>> truth(a) True >>> is_(a, b) False >>> is_not(a, b) True
>>> for func in (lt, le, eq, ne, ge, gt): print '%s(a, b):' % func.__name__, func(a, b) lt(a, b): True le(a, b): True eq(a, b): False ne(a, b): True ge(a, b): False gt(a, b): False
>>> a, b = -1, 5.0 >>> abs(a), neg(a) (1, 1) >>> add(a, b), div(a, b), floordiv(a, b), mod(a, b), pow(a, b), truediv(a, b) (4.0, -0.2, -1.0, 4.0, -1.0, -0.2) >>> c, d = 2, 6 >>> and_(c, d), invert(c), lshift(c, d), or_(c, d), rshift(d, c), xor(c, d) (2, -3, 128, 6, 1, 4)
处理序列的操作符可以划分为4组:建立序列,搜索元素,访问内容和从序列删除元素.
>>> a = [1, 2, 3] >>> b = ['a', 'b', 'c'] >>> concat(a, b) [1, 2, 3, 'a', 'b', 'c'] >>> repeat(a, 3) [1, 2, 3, 1, 2, 3, 1, 2, 3] >>> contains(a, 1) True >>> countOf(a, 1) 1 >>> indexOf(a, 1) 0 >>> getitem(b, 1) 'b' >>> getslice(a, 1, 3) [2, 3] >>> setitem(b, 1, 'd') >>> b ['a', 'd', 'c'] >>> delitem(b, 1) >>> b ['a', 'c']
通过attrgetter()来直接获取元素参数的属性:
from operator import * class MyObj(object): def __init__(self, arg): super(MyObj, self).__init__() self.arg = arg def __repr__(self): return 'MyObj(%s)' % self.arg lst = [MyObj(i) for i in range(5)] print 'objects:', lst g = attrgetter('arg') vals = [g(i) for i in lst] print 'arg values:', vals lst.reverse() print 'reversed :', lst print 'sorted :', sorted(lst, key = g)解释器显示如下:
>>> objects: [MyObj(0), MyObj(1), MyObj(2), MyObj(3), MyObj(4)] arg values: [0, 1, 2, 3, 4] reversed : [MyObj(4), MyObj(3), MyObj(2), MyObj(1), MyObj(0)] sorted : [MyObj(0), MyObj(1), MyObj(2), MyObj(3), MyObj(4)]而我们可以通过itemgetter()来获取列表或字典的值:
from operator import * lst = [dict(val = -1 * i) for i in range(4)] print 'Dictionaries:', lst g = itemgetter('val') vals = [g(i) for i in lst] print ' values:', vals print ' sorted:', sorted(lst, key=g) print解释器显示如下:
>>> Dictionaries: [{'val': 0}, {'val': -1}, {'val': -2}, {'val': -3}] values: [0, -1, -2, -3] sorted: [{'val': -3}, {'val': -2}, {'val': -1}, {'val': 0}]
operator模块中的函数通过相应操作的标准Python接口完成工作,所以它们不仅适用于内置类型,还适用于用户定义的类:
from operator import * class MyObj(object): def __init__(self, val): super(MyObj, self).__init__() self.val = val return def __str__(self): return 'MyObj(%s)' % self.val def __lt__(self, other): print 'Testing %s < %s' % (self, other) return self.val < other.val def __add__(self, other): print 'Adding %s + %s' % (self, other) return MyObj(self.val + other.val) a = MyObj(1) b = MyObj(2) print lt(a, b) print add(a, b)解释器显示如下:
>>> Testing MyObj(1) < MyObj(2) True Adding MyObj(1) + MyObj(2) MyObj(3)
operator模块还包括一些函数来测试映射,数字和序列类型的API兼容性
from operator import * class NoType(object): pass class MultiType(object): def __len__(self): return 0 def __getitem__(self, name): return 'mapping' def __init__(self): pass o = NoType() t = MultiType() for func in (isMappingType, isNumberType, isSequenceType): print '%s(o):' % func.__name__, func(o) print '%s(t):' % func.__name__, func(t)解释器显示如下:
>>> isMappingType(o): False isMappingType(t): True isNumberType(o): False isNumberType(t): False isSequenceType(o): False isSequenceType(t): True
作用:创建和处理上下文管理器的工具
Python版本:2.5及以后版本
上下文管理器要负责一个代码块中的资源,可能在进入代码块时创建资源,然后在退出代码块时清理这个资源.最常用的代码是读取文件内容:
>>> with open('test.txt') as fobj: fobj.read() '\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x04\x00\x00\x00'上下文管理器由with语句启用,这个API包含:__init__()初始化操作,__enter__()执行流进入with中时执行,__exit__()执行流离开with时执行:
class Context(object): def __init__(self): print '__init__()' def __enter__(self): print '__enter__()' return self def __exit__(self, exc_type, exc_val, exc_tb): print '__exit__()' with Context(): print 'Doing work in the context'解释器显示如下:
>>> __init__() __enter__() Doing work in the context __exit__()如果在with语句的as子句中指定了名称,__enter__()方法可以返回与这个名称相关联的任何对象.
class WithinContext(object): def __init__(self, context): print 'withincontext.__init__(%s)' % context def do_something(self): print 'WithinContext.do_something()' def __del__(self): print 'WithinContext.__del__' class Context(object): def __init__(self): print 'Context.__init__()' def __enter__(self): print 'Context.__enter__()' return WithinContext(self) def __exit__(self, exc_type, exc_val, exc_tb): print 'Context.__exit__()' with Context() as c: c.do_something()解释器显示如下:
>>> Context.__init__() Context.__enter__() withincontext.__init__(<__main__.Context object at 0x00000000029A0978>) WithinContext.do_something() Context.__exit__()而__exit__()方法接收一些参数,其中包含with块中产生的异常的详细信息:
class Context(object): def __init__(self, handle_error): print '__init__(%s)' % handle_error self.handle_error = handle_error def __enter__(self): print '__enter__()' return self def __exit__(self, exc_type, exc_val, exc_tb): print '__exit__()' print ' exc_type =', exc_type print ' exc_val =', exc_val print ' exc_tb =', exc_tb return self.handle_error with Context(True): raise RuntimeError('error mesage handled') print with Context(False): raise RuntimeError('error message propagated')为True情况下,__exit__()可以处理这个异常.如果为False,则继续抛出此异常:
>>> __init__(True) __enter__() __exit__() exc_type = <type 'exceptions.RuntimeError'> exc_val = error mesage handled exc_tb = <traceback object at 0x0000000002752E88> __init__(False) __enter__() __exit__() exc_type = <type 'exceptions.RuntimeError'> exc_val = error message propagated exc_tb = <traceback object at 0x0000000002753208> Traceback (most recent call last): File "C:\Python27\test.py", line 21, in <module> raise RuntimeError('error message propagated') RuntimeError: error message propagated
对于很少的上下文,完全没必要编写__enter__和__exit__方法.我们可以使用contextmanager()修饰符将一个生成器函数转换为上下文管理器.
import contextlib @contextlib.contextmanager def make_context(): print ' entering' try: yield() except RuntimeError, err: print ' ERROR:', err finally: print ' exiting' print 'Normal:' with make_context() as value: print ' inside with statement:', value print '\nhandled error:' with make_context() as value: raise RuntimeError('showing example of handling an error') print '\nunhandled error:' with make_context() as value: raise ValueError('this exception is not handled')解释器显示如下:
>>> Normal: entering inside with statement: () exiting handled error: entering ERROR: showing example of handling an error exiting unhandled error: entering exiting Traceback (most recent call last): File "C:\Python27\test.py", line 23, in <module> raise ValueError('this exception is not handled') ValueError: this exception is not handled
import contextlib @contextlib.contextmanager def make_context(name): print 'entering:', name yield name print 'exiting:', name with make_context('A') as A, make_context('B') as B: print 'inside with statement:', A, B解释器显示如下:
>>> entering: A entering: B inside with statement: A B exiting: B exiting: A
并不是所有的对象都像file类一样自动关闭对象,所以我们需要使用closing为它创建一个上下文管理器:
import contextlib class Door(object): def __init__(self): print '__init__()' def close(self): print 'close()' print 'Normal Example:' with contextlib.closing(Door()) as door: print 'inside with statement' print '\nError handling example:' try: with contextlib.closing(Door()) as door: print ' raising from inside with statement' raise RuntimeError('error message') except Exception, err: print ' Had an error:', err无论是否存在错误,这个句柄都会关闭:
>>> Normal Example: __init__() inside with statement close() Error handling example: __init__() raising from inside with statement close() Had an error: error message