Python 模块简介 -- functools
Python 的 functools 模块可以说主要是为函数式编程而设计,用于增强函数功能。
functools.partial
用于创建一个偏函数,它用一些默认参数包装一个可调用对象,返回结果是可调用对象,并且可以像原始对象一样对待,这样可以简化函数调用。实际上 partial 相当于一个高阶函数,其大致的实现如下(实际在标准库中它是用 C 实现的):
def partial(func, *args, **keywords):
def newfunc(*fargs, **fkeywords):
newkeywords = keywords.copy()
newkeywords.update(fkeywords)
return func(*(args + fargs), **newkeywords)
newfunc.func = func
newfunc.args = args
newfunc.keywords = keywords
return newfunc
一个简单的使用示例:
from functools import partial
def add(x, y):
return x + y
add_y = partial(add, 3) # add_y 是一个新的函数
add_y(4)
一个很实用的例子:
def json_serial_fallback(obj):
"""JSON serializer for objects not serializable by default json code"""
if isinstance(obj, (datetime.datetime, datetime.date)):
return str(obj)
if isinstance(obj, bytes):
return obj.decode("utf-8")
raise TypeError ("%s is not JSON serializable" % obj)
json_dumps = partial(json.dumps, default=json_serial_fallback)
可以在 json_serial_fallback
函数中添加类型判断来指定如何 json 序列化一个 Python 对象
functools.update_wrapper
用 partial 包装的函数是没有 __name__
和 __doc__
,这使得依赖于这些属性的代码可能无法正常工作。update_wrapper
可以拷贝被包装函数的 __name__
、__module__
、__doc__
和 __dict__
属性到新的封装函数中去,其实现也非常简单:
WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__doc__')
WRAPPER_UPDATES = ('__dict__',)
def update_wrapper(wrapper,
wrapped,
assigned = WRAPPER_ASSIGNMENTS,
updated = WRAPPER_UPDATES):
for attr in assigned:
setattr(wrapper, attr, getattr(wrapped, attr))
for attr in updated:
getattr(wrapper, attr).update(getattr(wrapped, attr, {}))
return wrapper
update_wrapper
主要用在装饰器函数中,以确保被装饰的保留原理的属性。示例:
def wrap(func):
def call_it(*args, **kwargs):
print "calling", func.__name__
return func(*args, **kwargs)
return call_it
@wrap
def hello():
print "hello"
from functools import update_wrapper
def wrap2(func):
def call_it(*args, **kwargs):
print "calling", func.__name__
return func(*args, **kwargs)
return update_wrapper(call_it, func)
@wrap2
def hello2():
print "hello2"
# Script starts from here
if __name__ == '__main__':
print hello.__name__ # call_it
print hello2.__name__ # hello2
functool.wraps
wraps
函数是为了在装饰器中方便的拷贝被装饰函数的签名,而对 update_wrapper
做的一个包装,其实现如下:
def wraps(wrapped,
assigned = WRAPPER_ASSIGNMENTS,
updated = WRAPPER_UPDATES):
return partial(update_wrapper, wrapped=wrapped,
assigned=assigned, updated=updated)
示例:
from functools import wraps
def wrap3(func):
@wraps(func)
def call_it(*args, **kwargs):
print "calling", func.__name__
return func(*args, **kwargs)
return call_it
@wrap3
def hello3(func):
print "hello3"
print hello3.__name__ # hello3
functools.reduce
在 Python2 中等同于内建函数 reduce,但是在 Python3 中内建的 reduce 函数被移除,只能使用 functools.reduce
。该函数的作用是将一个序列归纳为一个输出,其原型如下:
reduce(function, sequence, startValue)
使用示例:
>>> def foo(x, y):
... return x + y
...
>>> l = range(1, 10)
>>> reduce(foo, l)
45
>>> reduce(foo, l, 10)
55
functools.cmp_to_key
在 list.sort 和 内建函数 sorted 中都有一个 key 参数,这个参数用来指定取元素的什么值进行比较,例如按字符串元素的长度进行比较:
>>> x = ['hello','abc','iplaypython.com']
>>> x.sort(key=len)
>>> x
['abc', 'hello', 'iplaypython.com']
也就是说排序时会先对每个元素调用 key 所指定的函数,然后再排序。同时,sorted 和 list.sort 还提供了 cmp 参数来指定如何比较两个元素,但是在 Python 3 中该参数被去掉了。cmp_to_key
函数就是用来将老式的比较函数转化为 key 函数。用到 key 参数的函数还有 sorted(), min(), max(), heapq.nlargest(), itertools.groupby() 等。
functools.total_ordering
这个一个类装饰器,如果一个类实现了 __lt__
、__le__
、__gt__
、__ge__
这些方法中的至少一个,该装饰器会自动实现其他几个方法。示例:
from functools import total_ordering
@total_ordering
class Student:
def __init__(self, firstname, lastname):
self.firstname = firstname
self.lastname = lastname
def __eq__(self, other):
return ((self.lastname.lower(), self.firstname.lower()) ==
(other.lastname.lower(), other.firstname.lower()))
def __lt__(self, other):
return ((self.lastname.lower(), self.firstname.lower()) <
(other.lastname.lower(), other.firstname.lower()))
print dir(Student)
stu = Student("Huoty", "Kong")
stu2 = Student("Huoty", "Kong")
stu3 = Student("Qing", "Lu")
print stu == stu2
print stu > stu3
输出结果:
['__doc__', '__eq__', '__ge__', '__gt__', '__init__', '__le__', '__lt__', '__module__']
True
False
functools.lru_cache
这个装饰器是在 Python3 中新加的,在 Python2 中如果想要使用可以安装第三方库 functools32
。该装饰器用于缓存函数的调用结果,对于需要多次调用的函数,而且每次调用参数都相同,则可以用该装饰器缓存调用结果,从而加快程序运行。示例:
from functools import lru_cache
@lru_cache(None)
def add(x, y):
print("calculating: %s + %s" % (x, y))
return x + y
print(add(1, 2))
print(add(1, 2)) # 直接返回缓存信息
print(add(2, 3))
输出结果:
calculating: 1 + 2
3
3
calculating: 2 + 3
5
由于该装饰器会将不同的调用结果缓存在内存中,因此需要注意内存占用问题,避免占用过多内存,从而影响系统性能。
functools.singledispatch
单分发器,Python 3.4 新增,用于实现泛型函数,由一个单一参数的类型来决定调用哪个函数。示例:
@singledispatch
def fun(arg, verbose=False):
if verbose:
print("Let me just say", end=" ")
print(arg)
@fun.register(int)
def _(arg, verbose=False):
if verbose:
print("Strength in numbers, eh?", end=" ")
print(arg)
@fun.register(list)
def _(arg, verbose=False):
if verbose:
print("Enumerate this:")
for i, elem in enumerate(arg):
print(i, elem)
fun("Hello world.")
fun(18)
fun(["a", "b"])
输出:
Hello world.
18
0 a
1 b
一个还可以支持多个泛型:
@fun.register(float)
@fun.register(Decimal)
def _(arg, verbose=False):
print(arg)
fun.registry
中保存了所有的泛型函数,使用 fun.registry[float] 或者 fun.dispatch(float) 可以获取相应的函数:
>>> fun.registry
mappingproxy({int: <function singledispatch._>,
float: <function singledispatch._>,
list: <function singledispatch._>,
object: <function singledispatch.fun>,
decimal.Decimal: <function singledispatch._>})
>>> fun.dispatch(float)
<function singledispatch._>
>>> fun.registry[int]
<function singledispatch._>