functools模块可以作用于所有的可以被调用的对象,包括函数 定义了__call__方法的类等
1 functools.cmp_to_key(func)
将比较函数(接受两个参数,通过比较两个参数的大小返回负值,0,或者正数)转换为key function(返回一个值用来比较或者排序的可调用对象),
例如: sorted(iterable, functools.cmp_to_key(locale.strcoll))
def cmp1(n1, n2): return n1 - n2 a = [1, 6, 2, 9] print(sorted(a, key=functools.cmp_to_key(cmp1)))
2 @functools.lru_cache(maxsize=128, typed=False)
首先这是一个装饰器
其次,介绍一下LRU算法:
LRU是最常用的缓存算法,全称叫“Least Recently Used”,顾名思义,就是在缓存miss 并且缓存空间已满的时候,将最久没访问过的数据删除从而腾出空间。
然后,说一下这个装饰器的两个参数的含义:
maxsize: 表示缓存大小,如果设置为None,表示不限制,设置为0表示不启用缓存机制
typed:如果设置为True,则该装饰器所装饰的函数的参数即使值相等(比如说 3 == 3.0 ),但类型不同(一个是整型一个是浮点),也会被区分对待为不同的缓存
然后,说明一下这个装饰器对所装饰的函数的要求,
1 函数的参数接收的值必须是不可变对象,像字符串,数字,元组等都属于此列
2 其次函数返回的对象最好也是不可变对象,当然这一点没有硬性要求,但是道理大家懂。
来一个栗子:
@functools.lru_cache(2526) def get_resource(page): url = "https://urls_does_not_contain_pornographic_informations/%s" % page try: with urllib.request.urlopen(url) as s: return s.read() except urllib.error.HTTPError: return 'Not Found' for i in range(1, 2526): pep = get_resource(i) print(pep)
3 @functools.total_ordering
首先这是一个类装饰器,这个类装饰器要求它所定义的类中必须定义:
1 小于__lt__(), 小于等于__le__(),大于__gt__(),大于等于__ge__()中的一个
2 还要定义等于__eq__()方法。
只要我们按照要求定义了这些方法,该装饰器就会为我们完成其余的比较排序方法 。
4 functools.partial(func, *args, **keywords)
类似于这样:
def abc(a, b): print a + b def partial(func, *args, **kwargs): args_li = list(args) def inner(*nargs, **nkwargs): args_li.extend(nargs) kwargs.update(nkwargs) return func(*args_li, **kwargs) return inner new_abc = partial(abc, 2) new_abc(4)
实际上就是给某个函数加上几个固定参数然后返回一个新的函数,对于多个对象更新相同的值来说可以用到。比如:
from functools import partial class Test(object): def __init__(self): self.name = "lala" self.age = 20 def _update_attr(obj, update_dic): map(lambda item: setattr(obj, item[0], item[1]), update_dic.iteritems()) update_attr = partial(_update_attr, update_dic={"name": "mncu", "age": 18}) test_obj_list = [Test() for i in xrange(20)] map(update_attr, test_obj_list) for test_obj in test_obj_list: print test_obj.name, test_obj.age
5 class functools.partialmethod(func, *args, **keywords)
作用类似于上面的partial函数,但这个方法作用于类的方法,返回的是方法而不是函数。
>>> 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() >>> c.alive False >>> c.set_alive() >>> c.alive True
6 functool.update_wrapper(wrapper, wrapped[, assigned][, updated])
functools.wraps(wrapped[, assigned][, updated])
在python中,当一个函数被装饰器装饰后,这个函数名字对应的函数对象实际上是那个装饰器函数,也就是该函数名对应的的__name__以及__doc__实际上已经改变了,这就导致很难调试。而update_wrapper以及wraps就是用来解决这个问题。
#!/usr/bin/env python # encoding: utf-8 def wrap(func): def call_it(*args, **kwargs): """wrap func: call_it""" print 'before call' return func(*args, **kwargs) return call_it @wrap def hello(): """say hello""" print 'hello world' from functools import update_wrapper def wrap2(func): def call_it(*args, **kwargs): """wrap func: call_it2""" print 'before call' return func(*args, **kwargs) return update_wrapper(call_it, func) @wrap2 def hello2(): """test hello""" print 'hello world2' if __name__ == '__main__': hello() print hello.__name__ print hello.__doc__ print hello2() print hello2.__name__ print hello2.__doc__
结果:
before call
hello world
call_it
wrap func: call_it
before call
hello world2
hello2
test hello
from functools import wraps def wrap3(func): @wraps(func) def call_it(*args, **kwargs): """wrap func: call_it2""" print 'before call' return func(*args, **kwargs) return call_it @wrap3 def hello3(): """test hello 3""" print 'hello world3'
结果:
before call
hello world3
hello3
test hello 3
参考:
https://blog.theerrorlog.com/simple-lru-cache-in-python-3.html, 作者: Kay Zheng
http://www.wklken.me/posts/2013/08/18/python-extra-functools.html 作者:WKLKEN