2017-07-06-python中的functools
Table of Contents
1 partial
我觉得就是给了一些默认参数:
import functools def add(a, b): print '[a = %s,b = %s]' % (a, b) add(4, 2) plus3 = functools.partial(add, 3) plus5 = functools.partial(add, 5) plusplus = functools.partial(add, 5, 8) plus3(4) plus3(7) plus5(10) plusplus()
它的定义应该是像这样:
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
仔细看下下面这个代码的输出就知道它到底对这些函数都做了啥了:
def add(a, b, *args, **kargs): print '[a = %s,b = %s]' % (a, b) if args: print 'really add args = {f}'.format(f=args) if kargs: print 'really add kargs = %s' % kargs add(4, 2) def my_partial(func, *args, **keywords): print 'in my_partial: args = %s, keywords = %s' % (args, keywords) def newfunc(*fargs, **fkeywords): newkeywords = keywords.copy() newkeywords.update(fkeywords) print 'fkeywords in newfunc = %s' % fkeywords print 'fargs in newfunc = {f}'.format(f=fargs) print 'args in newfunc = {f}'.format(f=args) print 'keywords in newfunc = {f}'.format(f=keywords) print '[test] input args = {f}'.format(f=(args + fargs)) return func(*(args + fargs), **newkeywords) newfunc.func = func newfunc.args = args newfunc.keywords = keywords return newfunc plus3 = my_partial(add, 3, 12, name='peng') plus3(10, 11, name='xiepeng')
可以看到,通过它装钸过的函数,实际传给它的 *args
是装钸时的和实际
调用的之和,例子中实际传进来的就是 (3, 12, 10, 11)
。给到add函数
时,3和12分别给了a和b参数。剩下才放在 args
中(倒数第二句)。而
**kargs
这种,会在实际调用函数的时候覆盖装钸时的值,其实就是那个
newkeywords.update
做的事情。这里是 {'name': 'peng'}
被
{'name': 'xiepeng'}
替代。
2 update_wrapper
默认partial对象没有__name__和__doc__, 这种情况下,对于装饰器函数非 常难以debug.使用update_wrapper(),从原始对象拷贝或加入现有partial对 象。
直接使用 wrap ,原来的 hello的 __name__
和 __doc__
被wrap中的
call_it函数覆盖了。wrap2中使用updatea_wrapper,可以把原来的函数的这
两个属性搞回去。
#!/usr/local/bin/python 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__
3 cmp_to_key
对于python 2.X,支持一种叫 cmp
函数来排序:
>>> def numeric_compare(x, y): ... return x - y >>> sorted([5, 2, 4, 1, 3], cmp=numeric_compare) [1, 2, 3, 4, 5] Or you can reverse the order of comparison with: >>> def reverse_numeric(x, y): ... return y - x >>> sorted([5, 2, 4, 1, 3], cmp=reverse_numeric) [5, 4, 3, 2, 1]
python 2.4之后1,包括python 3都支持一种 key
的函数来
排序:
>>> sorted("This is a test string from Andrew".split(), key=str.lower) ['a', 'Andrew', 'from', 'is', 'string', 'test', 'This'] >>> student_tuples = [ ... ('john', 'A', 15), ... ('jane', 'B', 12), ... ('dave', 'B', 10), ... ] >>> sorted(student_tuples, key=lambda student: student[2]) # sort by age [('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]
但是在python 3的时候,为了代码的简化,删除了sorted对于 cmd
函数的
支持。所以对于python 3来说,可以使用 functools.cmp_to_key()
来把
一个 cmd
函数转为一个 key
函数。
4 total_ordering
一个class如果自定义比较函数的一个或者几个,这个装钸器可以帮你定义其 它的。但是注意:
The class must define one of __lt__(), __le__(), __gt__(), or __ge__(). In addition, the class should supply an __eq__() method. For example: @total_ordering class Student: 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()))
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())) a = Student('A', 'C') b = Student('b', 'd') c = Student('b', 'c') d = Student('b', 'c') print a > b print a <= b print d >= c
感觉amazing。
5 wraps
它是 update_wrapper 的一个更加方便的形式,可以这样使用:
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__
可以看到,函数的name还是原来定义的,而不是里面wrapper函数的内容。
6 TODO partial创建的对象 暂时没有找到合适的例子
partial对象是在调用 partial()
函数后就创建的,它们有下面这三种只读
的属性:
- partial.func :这一定是一个可以被调用的对象或者函数,对partial对 象的调用实际就是调用它。
- partial.args :定义partial函数时所有最左边的不是赋值的参数
- partial.keywords :定义partial函数时所有赋值传进来的参数
from functools import partial def add(x, y, name=None): print 'result = %s' % (x + y) plus = partial(add, 3, 4, name='test') print add print plus print plus.func print plus.args print plus.keywords plus()
partial对象和函数对象差不多:能被调用,弱引用的,可以有属性。不过也 有一些重要的区别: