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

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值