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

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

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

1.修饰符

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

  partial对象

  下面这个例子显示了函数myfunc()的两个简单的partial对象。show_details()的输出包含这个部分对象的func,args和keywords的属性。

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 balues 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 arguments:')

  获取函数属性

  默认情况下,partial对象没有__name__或__doc__属性。如果没有这些属性,写实的函数将更难调试。使用updata_wrapper()可以从原函数将属性复制或添加到partial对象。

def myfunc(a, b=2):
    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:
        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)
functools.update_wrapper(p1, myfunc)
show_details('updated wrapper', p1)

  添加到包装器的属性在WRAPPER_ASSIGNMENTS中定义,而WRAPPER_UPDATES列出了要修改的值。

myfunc:
object: <function myfunc at 0x00000000022E9B88>
__name__
myfunc
__doc__ None
**********************************
raw wrapper:
object: functools.partial(<function myfunc at 0x00000000022E9B88>, b=4)
__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__', '__qualname__', '__doc__', '__annotations__')
update: ('__dict__',)
updated wrapper:
object: functools.partial(<function myfunc at 0x00000000022E9B88>, b=4)
__name__
myfunc
__doc__ None
**********************************

  其他可回调对象

  Partial适用于任何可回调对象,而不只是单独的函数。

class MyClass():
    ''' Demonstration class for functools'''
    def method1(self,a,b=2):
        '''Docstring for method1()'''
        print('called method1 with:',(self,a,b))
    def method2(self,c,d=5):
        '''Docstirng for method2'''
        print('called method2 with:',(self,c,d))
    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))
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__))
o = MyClass()
show_details('method1 straight', o.method1)
o.method1('no default for a', b=3)
print('*********************11111111***********************')
p1 = functools.partial(o.method1,b=4)
functools.update_wrapper(p1,o.method1)
show_details('method1 wrapper',p1)
p1('a goes here')
print('*********************22222222*********************')
show_details('method2', o.method2)
o.method2('no default for c',d=6)
print('*********************33333333*********************')
show_details('wrapped method2', o.wrapped_method2)
o.wrapped_method2('no default for c',d=6)
print('*********************44444444*********************')

  这个例子由一个实例以及实例的方法来创建部分对象。

method1 straight:
object: <bound method MyClass.method1 of <__main__.MyClass object at 0x0000000003E199C8>>
__main__
method1
__doc__ 'Docstring for method1()'
called method1 with: (<__main__.MyClass object at 0x0000000003E199C8>, 'no default for a', 3)
*********************11111111***********************
method1 wrapper:
object: functools.partial(<bound method MyClass.method1 of <__main__.MyClass object at 0x0000000003E199C8>>, b=4)
__main__
method1
__doc__ 'Docstring for method1()'
called method1 with: (<__main__.MyClass object at 0x0000000003E199C8>, 'a goes here', 4)
*********************22222222*********************
method2:
object: <bound method MyClass.method2 of <__main__.MyClass object at 0x0000000003E199C8>>
__main__
method2
__doc__ 'Docstirng for method2'
called method2 with: (<__main__.MyClass object at 0x0000000003E199C8>, 'no default for c', 6)
*********************33333333*********************
wrapped method2:
object: functools.partial(<function MyClass.method2 at 0x0000000002556318>, 'wrapped c')
__main__
method2
__doc__ 'Docstirng for method2'
called method2 with: ('wrapped c', 'no default for c', 6)
*********************44444444*********************

  为修饰符获取函数属性

  在修饰符中使用时,更新包赚的可回调对象的属性尤其有用,因为变换后的函数最后会得到原‘裸’函数的属性。

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):
    @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):
    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):
    myfunc(a,b)
    return
show_details('decorated_myfunc', decorated_myfunc)
decorated_myfunc()
decorated_myfunc('args to decorated',4)

  functools提供了一个修饰符wraps()。它会对所修饰的函数应用update_wrapper()。

myfunc:
object: <function myfunc at 0x00000000025CB438>
__name__:
myfunc
__doc__ None
****************************
myfunc: ('unwrapped,default b', 2)
myfunc: ('unwrapped,passing b', 3)
*****************************
wrapped_myfunc:
object: <function myfunc at 0x00000000025CBC18>
__name__:
myfunc
__doc__ None
****************************
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 0x00000000123FD0D8>
__name__:
decorated_myfunc
__doc__ None
****************************
decorated: ('decorated defaults', 1)
******************************
myfunc: ('decorated defaults', 1)
decorated: ('args to decorated', 4)
******************************
myfunc: ('args to decorated', 4)

  

2.比较

  在python2中,类可以定义一个__cmp__()方法,后者会根据这个对象小于,等于还是大于所比较的元素返回-1,0或1.python2.1引入了富比较(rich comparsion)方法API(__lt__(),__le__(),__eq__(),__ne__(),__gt__()和__ge__()),可以完成一个比较操作并返回一个Boolean值。python3则废弃了__cmp__()而代之以这些新方法。所以functools提供了一些工具,以便于编写python2类而且同时符合python3中新的比较需求。

  富比较

  设计富比较API是为了支持涉及复杂比较的类,从而以最高效的方式实现各个测试。不过,对于比较相对简单的类,手动地创建各个富比较方法就没有必要了。total_ordering()类修饰符取一个提供了部分方法的类,并添加其余的方法。

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

  这个类必须提供__eq__()和另一个富比较方法的实现。修饰符会增加其余方法的实现,它们利用类提供的比较来完成工作。

Methods:

[]

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

  注:在python中,eval()方法是一个经常用到的函数,我们在编写输入函数的时候,需要把input()函数写进eval()方法中,这样得到的输入结果就不会是字符串类型的了。

 

  比对序

  由于python3中废弃了老式比较函数,因此sort()之类的函数中也不再支持cmp参数。对于使用了比较函数的python2程序,可以用cmp_to_key()将它们转换为一个返回比对键(collation key)的函数,这个键用于确定元素在最终序列中的位置。

 

 

 

 

 

 

 

  

 

posted @ 2020-03-28 20:28  King~~~  阅读(199)  评论(0编辑  收藏  举报