欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Python标准库学习笔记3:算法

程序员文章站 2022-07-12 13:54:12
...

1. functools---管理函数的工具

作用:处理其他函数的函数

Python版本:2.5及以后版本

    functools模块提供了一些工具来调整或扩展函数和其他可回调对象,而不必完全重写.

1. 修饰符

    functools模块提供的主要工具是partial类,它可以用来"包装"一个有默认参数的可回调对象.得到的对象本身是可回调的,可以看作就像是原来的函数.它与原函数的参数完全相同,调用时也可以提供额外的位置或命名参数.可以使用partial而不是lambda为函数提供默认参数,有些参数可以不指定.

partial对象

    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. 需要深入理解装饰器,才能理解以上代码.

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)

2. itertools---迭代器函数

作用:itertools模块包含一组函数用于处理序列数据集

Python版本:2.3及以后版本

    与使用列表的代码相比,基于迭代器的算法可以提供更好的内存使用特性.在真正需要数据之前,并不从迭代器生成数据,由于这个原因,不需要将所有数据都同时存储在内存中.这种"懒"处理模型可以减少内存使用,相应的还可以减少交换以及大数据集的其他副作用,从而改善性能.

1. 合并和分解迭代器

    chain函数取多个迭代器作为参数,最后返回一个迭代器,它能生成所有输入迭代器的内容,就好像这些内容来自一个迭代器一样.

    利用chain(),可以轻松的处理多个序列而不必构造一个大的列表.

>>> from itertools import *
>>> for i in chain([1, 2, 3], ['a', 'b', 'c']):
	print i,

	
1 2 3 a b c
    izip()返回一个迭代器,它会把多个迭代器的元素结合到一个元组中.类似于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

2. 转换输入

    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 = 36
    starmap()函数类似于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

3. 生成新值

    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)]


4. 过滤

    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: 2
    ifilter和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]


5. 数据分组

    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)]


3. operator---内置操作符的函数接口

作用:内置操作符的函数接口

Python版本:1.4及以后版本

1. 逻辑操作


>>> a = -1
>>> b = 5
>>> from operator import *
>>> not_(a)
False
>>> truth(a)
True
>>> is_(a, b)
False
>>> is_not(a, b)
True


2. 比较操作符


>>> 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


3. 算术操作符


>>> 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. 序列操作符

    处理序列的操作符可以划分为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']


5. 属性和元素"获取方法"

    通过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}]


6. 结合操作符和定制类

    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)


7. 类型检查

    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


4. contextlib---上下文管理器工具

作用:创建和处理上下文管理器的工具

Python版本:2.5及以后版本

1. 上下文管理器API

    上下文管理器要负责一个代码块中的资源,可能在进入代码块时创建资源,然后在退出代码块时清理这个资源.最常用的代码是读取文件内容:


>>> 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


2. 从生成器到上下文管理器

    对于很少的上下文,完全没必要编写__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


3. 嵌套上下文


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


4. 关闭打开的句柄

    并不是所有的对象都像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



转载于:https://my.oschina.net/voler/blog/382107