Python标准模块——Functools

概述

可调用对象的高阶函数和操作

functools模块用于高阶函数:作用于或返回其他函数的函数。一般来说,对于这个模块,任何可调用对象都可以被视为函数。

functools 模块定义了以下函数︰

functools.cmp_to_key(func)

将旧风格的比较函数转换为key函数用于接受key函数的工具(例如sorted()min()max()heapq.nlargest )heapq.nsmallest()itertools.groupby())。此函数主要用作从支持使用比较函数的Python 2转换的程序的过渡工具。

比较函数是任何一个可调用的函数,且包含两个参数,对参数进行比较,如果小于返回负数,等于返回0,大于返回正数。Key函数是一个可调用对象,它接受一个参数并返回另一个值作为排序的键。

示例:

sorted(iterable, key=cmp_to_key(locale.strcoll))  # locale-aware sort order
from functools import cmp_to_key
from builtins import sorted
def compare(ele1,ele2):
    return ele2 - ele1

a = [2,3,1]
print sorted(a, key = cmp_to_key(compare))
@functools.lru_cache(maxsize=128, typed=False)

装饰器用一个memoizing可调用函数来包装一个函数,它可以保存最近的maxsize个调用。当使用相同的参数定期调用昂贵的或I / O绑定的函数时,可以节省时间。

由于字典用于缓存结果,函数的位置和关键字参数必须是可哈希的。

如果maxsize设置为None,则禁用LRU功能,并且缓存可以无限制增长。当maxsize是二的幂时,LRU功能执行得最好。

如果类型设置为True,则不同类型的函数参数将单独缓存。例如,f(3)f(3.0)将被视为具有不同结果的不同调用。

To help measure the effectiveness of the cache and tune the maxsize parameter, the wrapped function is instrumented with a cache_info() function that returns a named tuple showing hits, misses, maxsize and currsize. 在多线程环境中,命中和未命中是近似的。

装饰器还提供了用于清除或使缓存无效的cache_clear()函数。

原始的底层函数可以通过__wrapped__属性访问。这对于内省,绕过缓存或者用不同的缓存重新封装函数很有用。

当最近的呼叫是即将来电的最佳预测因子时,LRU(最近最少使用)高速缓存效果最好(例如,新闻服务器上最受欢迎的文章往往每天都更改)。缓存的大小限制确保缓存不会在长时间运行的进程(如Web服务器)上不受限制地增长。

用于静态Web内容的LRU缓存示例:

@lru_cache(maxsize=32)
def get_pep(num):
    'Retrieve text of a Python Enhancement Proposal'
    resource = 'http://www.python.org/dev/peps/pep-%04d/' % num
    try:
        with urllib.request.urlopen(resource) as s:
            return s.read()
    except urllib.error.HTTPError:
        return 'Not Found'

>>> for n in 8, 290, 308, 320, 8, 218, 320, 279, 289, 320, 9991:
...     pep = get_pep(n)
...     print(n, len(pep))

>>> get_pep.cache_info()
CacheInfo(hits=3, misses=8, maxsize=32, currsize=8)

使用高速缓存实现动态规划技术有效计算斐波纳契数的示例:

@lru_cache(maxsize=None)
def fib(n):
    if n < 2:
        return n
    return fib(n-1) + fib(n-2)

>>> [fib(n) for n in range(16)]
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610]

>>> fib.cache_info()
CacheInfo(hits=28, misses=16, maxsize=None, currsize=16)

partial

functools.partial(func, *args, **keywords),函数装饰器,返回一个新的partial对象。调用partial对象和调用被修饰的函数func相同,只不过调用partial对象时传入的参数个数通常要少于调用func时传入的参数个数。当一个函数func可以接收很多参数,而某一次使用只需要更改其中的一部分参数,其他的参数都保持不变时,partial对象就可以将这些不变的对象冻结起来,这样调用partial对象时传入未冻结的参数,partial对象调用func时连同已经被冻结的参数一同传给func函数,从而可以简化调用过程。

如果调用partial对象时提供了更多的参数,那么他们会被添加到args的后面,如果提供了更多的关键字参数,那么它们将扩展或者覆盖已经冻结的关键字参数。

partial对象的调用如下,

import functools

def add(a,b):
    return a + b

add3 = functools.partial(add,3)
add5 = functools.partial(add,5)

print add3(4)

print add5(10)

控制台输出,

7
15

2.3 reduce

与Python内置的reduce函数一样,为了向Python3过渡;

import functools

a = range(1,6)
print functools.reduce(lambda x,y:x+y,a)

控制台输出,

15

2.4 total_ordering

这是一个类装饰器,给定一个类,这个类定义了一个或者多个比较排序方法,这个类装饰器将会补充其余的比较方法,减少了自己定义所有比较方法时的工作量;

被修饰的类必须至少定义 __lt__(), __le__(),__gt__(),__ge__()中的一个,同时,被修饰的类还应该提供 __eq__()方法。

from functools import total_ordering

class Person:
    # 定义相等的比较函数
    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()))

p1 = Person()
p2 = Person()

p1.lastname = "123"
p1.firstname = "000"

p2.lastname = "1231"
p2.firstname = "000"

print p1 < p2
print p1 <= p2
print p1 == p2
print p1 > p2
print p1 >= p2

控制台输出,

True
True
False
False
False
 

2.5 update_wrapper

更新一个包裹(wrapper)函数,使其看起来更像被包裹(wrapped)的函数。

可选的参数指定了被包裹函数的哪些属性直接赋值给包裹函数的对应属性,同时包裹函数的哪些属性要更新而不是直接接受被包裹函数的对应属性,参数assigned的默认值对应于模块级常量WRAPPER_ASSIGNMENTS(默认地将被包裹函数的 __name__, __module__,和 __doc__ 属性赋值给包裹函数),参数updated的默认值对应于模块级常量WRAPPER_UPDATES(默认更新wrapper函数的 __dict__ 属性)。

这个函数的主要用途是在一个装饰器中,原函数会被装饰(包裹),装饰器函数会返回一个wrapper函数,如果装饰器返回的这个wrapper函数没有被更新,那么它的一些元数据更多的是反映wrapper函数定义的特征,无法反映wrapped函数的特性。

2.6 wraps

这个函数可用作一个装饰器,简化调用update_wrapper的过程,调用这个函数等价于调用partial(update_wrapper, wrapped = wrapped, assigned = assigned,updated = updated)。

from functools import wraps

def my_decorator(f):
    @wraps(f)
    def wrapper(*args,**kwds):
        print "Calling decorated function"
        return f(*args,**kwds)
    return wrapper

@my_decorator
def example():
    """DocString"""
    print "Called example function"

example()
print example.__name__
print example.__doc__

控制台输出,

Calling decorated function
Called example function
example
DocString

可以看到,最终调用函数example时,是经过 @my_decorator装饰的,装饰器的作用是接受一个被包裹的函数作为参数,对其进行加工,返回一个包裹函数,代码使用 @functools.wraps装饰将要返回的包裹函数wrapper,使得它的 __name__, __module__,和 __doc__ 属性与被装饰函数example完全相同,这样虽然最终调用的是经过装饰的example函数,但是某些属性还是得到维护。

如果在 @my_decorator的定义中不使用 @function.wraps装饰包裹函数,那么最终example.__name__ 将会变成'wrapper',而example.__doc__ 也会丢失。

将 @wraps(f)注释掉,然后运行程序,控制台输出,

Calling decorated function
Called example function
wrapper
None

3 Reference

PYTHON-进阶-FUNCTOOLS模块小结

Python——functools

python中functools宝库下的partial

functools

原文地址:https://www.cnblogs.com/huyangblog/p/8686019.html