python中functools_Python-functools详解

一、简介

functools,用于高阶函数:指那些作用于函数或者返回其它函数的函数,通常只要是可以被当做函数调用的对象就是这个模块的目标。

在Python 2.7 中具备如下方法,

cmp_to_key,将一个比较函数转换关键字函数;(Python 3 不支持)

partial,针对函数起作用,并且是部分的;

reduce,与python内置的reduce函数功能一样;

total_ordering,在类装饰器中按照缺失顺序,填充方法;

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

wraps,可用作一个装饰器,简化调用update_wrapper的过程;

二、方法

1、cmp_to_keyfunctools.cmp_to_key(func)

将老式鼻尖函数转换成key函数,用在接受key函数的方法中(such as sorted(), min(), max(), heapq.nlargest(), heapq.nsmallest(), itertools.groupby())

一个比较函数,接收两个参数,小于,返回负数,等于,返回0,大于返回整数

key函数,接收一个参数,返回一个表明该参数在期望序列中的位置

2、partialfunctools.partial用于部分应用一个函数,它基于一个函数创建一个可调用对象,把原函数的某些参数固定,调用时只需要传递未固定的参数即可。

import functools

def add(a, b):

print("当前结果值", a+b)

add = functools.partial(add, 1)

add(2)

# 输出

当前结果值3

add函数原本接收两个参数a和b,经过partial包装之后,a参数的值被固定为了1,新的add对象(注意此处add已经是一个可调用对象,而非函数,下文分析源码会看到)只需要接收一个参数即可。

通俗点说:就是把原函数的部分参数固定了初始值,新的调用只需要传递其它参数。

下面来分析partial的源码(Python3.7),只摘录了核心部分:

class partial:

"""New function with partial application of the given arguments

and keywords.

"""

__slots__ = "func", "args", "keywords", "__dict__", "__weakref__"

def __new__(*args, **keywords):

if not args:

raise TypeError("descriptor '__new__' of partial needs an argument")

if len(args) < 2:

raise TypeError("type 'partial' takes at least one argument")

cls, func, *args = args

if not callable(func):

raise TypeError("the first argument must be callable")

args = tuple(args)

if hasattr(func, "func"):

args = func.args + args

tmpkw = func.keywords.copy()

tmpkw.update(keywords)

keywords = tmpkw

del tmpkw

func = func.func

self = super(partial, cls).__new__(cls)

self.func = func

self.args = args

self.keywords = keywords

return self

def __call__(*args, **keywords):

if not args:

raise TypeError("descriptor '__call__' of partial needs an argument")

self, *args = args

newkeywords = self.keywords.copy()

newkeywords.update(keywords)

return self.func(*self.args, *args, **newkeywords)

通过重写“_new__”方法,自定义对象实例化过程。

1、元组拆包,获取到传入的原函数(func)和需要固定的参数(args)

cls, func, *args = args

2、主要是为了支持嵌套调用,即add=partial(partial(add,1),2)这种情况,可先看第三步,回过头再来看

if hasattr(func, "func"):

args = func.args + args

tmpkw = func.keywords.copy()

tmpkw.update(keywords)

keywords = tmpkw

del tmpkw

func = func.func

3、实例化partial对象,将传入的函数和参数设置为当前对象的属性

self = super(partial, cls).__new__(cls)

self.func = func

self.args = args

self.keywords = keywords

return self

到这里我们已经明白了partial是怎么保存原函数和固定参数的了,下面来看一下调用的时候是如何执行的。

先简单了解一下可调用对象:当一个类实现了"__call__"方法后,这个类的对象就能够像函数一样被调用。

class Callable:

def __call__(self, a, b):

return a + b

func = Callable()

result = func(2, 3) # 像函数一样调用

print(result)

输出:5

好啦,我们看下partial的"__call__"是如何实现的:

def __call__(*args, **keywords):

if not args:

raise TypeError("descriptor '__call__' of partial needs an argument")

self, *args = args

newkeywords = self.keywords.copy()

newkeywords.update(keywords)

return self.func(*self.args, *args, **newkeywords)

1、元组拆包,获取到传入的非固定参数args

self, *args = args

2、拷贝当前对象的keywords参数,并且合并传入的非固定参数字典

newkeywords = self.keywords.copy()

newkeywords.update(keywords)

3、调用当前对象的func属性,func即被partial包装的原函数,同时传入暂存的固定参数self.args以及新传入的其它参数。

至此一切真相大白:partial通过实现"__new__"和"__call__"生成一个可调用对象,这个对象内部保存了被包装函数以及固定参数,这个对象可以像函数一样被调用,调用时,其实是执行了对象内部持有的被包装函数,其参数由固定参数和新传入的参数组合而来。、

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

4、total_ordering这个装饰器是在python2.7的时候加上的,它是针对某个类如果定义了lt、le、gt、ge这些方法中的至少一个,使用该装饰器,则会自动的把其他几个比较函数也实现在该类中

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 # True

print p1 <= p2 # True

print p1 == p2 # False

print p1 > p2 # False

print p1 >= p2 # False

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

下面来看update_wrapper的源码:

def update_wrapper(wrapper,

wrapped,

assigned = WRAPPER_ASSIGNMENTS,

updated = WRAPPER_UPDATES):

"""Update a wrapper function to look like the wrapped function

wrapper is the function to be updated

wrapped is the original function

assigned is a tuple naming the attributes assigned directly

from the wrapped function to the wrapper function (defaults to

functools.WRAPPER_ASSIGNMENTS)

updated is a tuple naming the attributes of the wrapper that

are updated with the corresponding attribute from the wrapped

function (defaults to functools.WRAPPER_UPDATES)

"""

for attr in assigned:

try:

value = getattr(wrapped, attr)

except AttributeError:

pass

else:

setattr(wrapper, attr, value)

for attr in updated:

getattr(wrapper, attr).update(getattr(wrapped, attr, {}))

# Issue #17482: set __wrapped__ last so we don't inadvertently copy it

# from the wrapped function when updating __dict__

wrapper.__wrapped__ = wrapped

# Return the wrapper so this can be used as a decorator via partial()

return wrapper

代码很简洁,就是把wrapped函数的属性拷贝到wrapper函数中。

wrapped是被装饰的原函数

wrapper是被装饰器装饰后的新函数。

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

下面来看update_wrapper的源码:

def wraps(wrapped,

assigned = WRAPPER_ASSIGNMENTS,

updated = WRAPPER_UPDATES):

"""Decorator factory to apply update_wrapper() to a wrapper function

Returns a decorator that invokes update_wrapper() with the decorated

function as the wrapper argument and the arguments to wraps() as the

remaining arguments. Default arguments are as for update_wrapper().

This is a convenience function to simplify applying partial() to

update_wrapper().

"""

return partial(update_wrapper, wrapped=wrapped,

assigned=assigned, updated=updated)

wrapped:指被装饰器装饰的原函数,我们的装饰器便是要拷贝它的属性。

assigned:要被重新赋值的属性列表,默认为WRAPPER_ASSIGNMENTS,可自定义传入

WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__qualname__', '__doc__',

'__annotations__')

updated:要被合并的属性列表,默认为WRAPPER_UPDATES,可自定义传入

WRAPPER_UPDATES = ('__dict__',)

返回值:

返回了一个partial对象,这个对象对update_wrapper进行了包装,固定了wrapped,assigned,updated三个参数。

wraps本省就是一个装饰器,因为它返回的是一个“函数”即partial对象,这个对象接收函数作为参数,同时以函数作为返回值。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值