functools模块处理的对象都是其他的函数,任何可调用对象都可以被视为用于此模块的函数。
1. functools.cmp_to_key(func)
因为Python3不支持比较函数,cmp_to_key就是将老式的比较函数(comparison function)转换成关键字函数(key function),与能够接受key function的函数一起使用,比如说sorted,list.sort, min, max, heapq.nlargest, itertools.groupby等等。
例子:
from functools import cmp_to_key def compare(x1, x2): return x1 - x2 a = [2, 3, 1] print(sorted(a, key=cmp_to_key(compare))) a.sort(key=cmp_to_key(compare)) print(a)
输出:
[1, 2, 3] [1, 2, 3]
2. @functools.lru_cache(maxsize=128, typed=False)
lru_cache可以作为装饰器将函数计算耗时的结果缓存起来,用来节省时间。
由于是使用字典进行的缓存,因此函数的关键字参数和位置参数必须是可哈希的。
如果maxsize=None,禁用lru功能,并且缓存可以无限制的增长。
当maxsize为2的幂次方时,lru的性能最好。
如果将typed设置为true,将单独缓存不同类型的函数参数,比如F(3)和F(3.0)将被视为不同结果的不同调用。
cache_info()函数可以用来测量缓存的有效性和优化maxsize参数。该函数返回一个命中、未命中、maxSize和currSize的命名的元组。
例子:
from functools import lru_cache @lru_cache(maxsize=32) def get_pep(num): 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)) print(get_pep.cache_info()) @lru_cache(maxsize=None) def fib(n): if n < 2: return n return fib(n-1) + fib(n - 2) print([fib(n) for n in range(16)]) print(fib.cache_info())
3. @functools.total_ordering
指定一个已经定义了一个或者多个比较排序方法的类,这个类修饰器提供其余的方法。
这个类必须已经定义了__lt__(), __le__(), __gt__(), __ge__()中的一个,并且定义了__eq__()
from functools import total_ordering @total_ordering class Student: def __init__(self, first_name, last_name): self.first_name = first_name self.last_name = last_name @staticmethod def _is_valid_operand(other): return hasattr(other, "last_name") and hasattr(other, "first_name") def __eq__(self, other): if not self._is_valid_operand(other): return NotImplemented return ((self.last_name.lower(), self.first_name.lower()) == (other.last_name.lower(), other.first_name.lower())) def __lt__(self, other): if not self._is_valid_operand(other): return NotImplemented return ((self.last_name.lower(), self.first_name.lower()) < (other.last_name.lower(), other.first_name.lower())) a = Student(first_name="tom", last_name="hancs") b = Student(first_name="tom", last_name="hancs") print(a == b) print(a <= b) print(a >= b)
如果不使用total_ordering对装饰器进行装饰的话,使用<=或者>=会报错:
Traceback (most recent call last): File "D:/LearnProject/performance/functools_test.py", line 33, in <module> print(a <= b) TypeError: ‘<=‘ not supported between instances of ‘Student‘ and ‘Student‘
4. functools.partial(func, *args, **keywords)
partial()用于冻结函数参数或者关键的其中一部分,生成一个简化了参数传入的新的函数对象。
例子:
from functools import partial basetwo = partial(int, base=2) print(basetwo(‘111‘))
5. functools.partialmethod(func, *args, **keywords)
功能与partial类似,用法如下:
from functools import partialmethod class Cell(object): def __init__(self): self._alive = False @property def alive(self): return self._alive def set_state(self, state): self._alive = bool(state) set_alive = partialmethod(set_state, True) set_dead = partialmethod(set_state, False) c = Cell() print(c.alive) c.set_alive() print(c.alive)
6. reduce(function, iterable[,initializer])
将两个参数的函数从左到右累计应用于序列项,比如reduce(lambda x, y:x+y, [1, 2, 3, 4, 5]),相当于计算(((1+2)+3)+4)+5。
from functools import reduce print(reduce(lambda x, y: x+y, range(0, 10)))
7. @functools.singledispatch
当有一个函数需要根据传入的变量的类型来判断需要输出的内容时,通常的做法是在函数内部使用大量的if/elif/else来解决问题。
这样做会使代码显得笨重,难以维护,也不便于扩展。
functools.singledispatch方法就是用于处理这个问题的
用法:
from functools import singledispatch @singledispatch def func(arg, verbose=False): if verbose: print("Let me just say,", end=" ") print(arg) @func.register(int) def _(arg, verbose=False): if verbose: print("Strength in numbers, eh?", end=" ") print(arg) @func.register(list) def _(arg, verbose=False): if verbose: print("Enumerate this:") for i, elem in enumerate(arg): print(i, elem) def nothing(arg, verbose=False): print("Nothing.") func.register(type(None), nothing) @func.register(float) def fun_num(arg, verbose=False): if verbose: print("Half of your number:", end=" ") print(arg / 2) func("Hello, world.") func("test.", verbose=True) func(42, verbose=True) func(["spam", "spam", "eggs", "spam"], verbose=True) func(None) func(1.23) # 检查泛型函数将为给定类型选择哪个实现 print(func.dispatch(float)) print(func.dispatch(dict)) # 访问所有已经注册的实现 print(func.registry.keys()) print(func.registry[float])
8. functools.update_wrapper(wrapper, wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)
用来更新被装饰函数的__name__,__doc__等信息,使其看起来像被装饰的函数
from functools import update_wrapper def wrap(func): def call_it(*args, **kwargs): """call it""" return func(*args, **kwargs) return call_it @wrap def hello(): """say hello""" print("hello world") def wrap2(func): def call_it2(*args, **kwargs): """call it2""" return func(*args, **kwargs) return update_wrapper(call_it2, func) @wrap2 def hello2(): """say hello2""" print("hello world2") print(hello.__name__) print(hello.__doc__) print(hello2.__name__) print(hello2.__doc__)
9. @functools.wraps(wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)
使用了wraps的装饰器可以保留被装饰函数的属性
from functools import wraps def my_decorator(f): @wraps(f) def wrapper(*args, **kwargs): print("Calling decorated function") return f(*args, **kwargs) return wrapper @my_decorator def example(): """Example Docstring""" print("Called example function") example() print(example.__name__) print(example.__doc__)
文章中的例子来自Python官方文档和网络。