函数8:高阶函数

目录

1. 高阶函数基础

2. 累计函数:reduce()

3. 偏函数:partial()

4. 函数装饰器:@wraps


1. 高阶函数基础

谓高阶函数,就是一个函数接收另一个函数作为参数,如内置函数map()、filter()、min()、max()、sorted()等均为高阶函数。

在模块 functools 里有很多实用的高阶函数和装饰器,本文将讲解 functools 模块中较常用的累计函数 reduce()、偏函数 partial()和装饰器 @wraps。

  • reduce() :将可迭代对象元素作为参数依次应用到函数中,常用于求和或求积;
  • partial():用于对指定函数进行“二次”包装,是闭包的简易实现;
  • @wraps:用于对装饰器进行装饰,方便获取被包装的函数属性。

2. 累计函数:reduce()

reduce() 函数格式如下:

functools.reduce(function, iterable[, initializer])

将 iterable 可迭代对象中的元素作为参数依次应用到 function 函数中,最终返回累计的结果。

如果存在 initializer 参数,它将被放在 iterable 的元素之前,并在 iterable 参数为空时作为默认值使用;如果没有指定 initializer 参数,并且 iterable 只有一个元素,则返回该元素的值。 

实现原理大致等价于:  

def reduce(function, iterable, initializer=None):
    it = iter(iterable)
    if initializer is None:
        value = next(it)
    else:
        value = initializer
    for element in it:
        value = function(value, element)
    return value

reduce() 只支持有两个参数的函数作为参数输入,输入参数小于或大于2的函数都报错。

#输入带有三个参数的函数,报错
def add3(x,y,z):
...     return x+y+z
... 
import functools
functools.reduce(add3,[1,2,3,4])
Traceback (most recent call last):
  File "<input>", line 1, in <module>
TypeError: add3() missing 1 required positional argument: 'z'

#输入只有一个参数的函数,报错
def fun1(x):
...     return x*x
... 
functools.reduce(fun1,[1,2,3,4])
Traceback (most recent call last):
  File "<input>", line 1, in <module>
TypeError: fun1() takes 1 positional argument but 2 were given

initializer 为空时,以序列的第一个元素作为输入函数的参数1,第二个元素作为参数2,并第一次执行输入函数;随后以第一次执行结果为参数1,第三个元素为参数2,第二次执行输入函数;循环往复,直到序列元素使用完毕;

initializer 不为空时,则以 initializer 为输入函数的参数1,序列的第一个元素为参数2,第一次执行输入函数,后续则与前述循环一致;可视为序列新增 initializer值 为第一个元素,再将 initializer 置为空。

#作为参数的函数
def calculate(var1,var2):
...     return (var2 - var1) * var1
... 
import functools

#initializer 为空时,执行 reduce() 函数 
functools.reduce(calculate,[2,1,5])
-14
#实际运算过程
(5 - ((1 - 2) * 2)) * ((1 - 2) * 2)
-14

#initializer 为0时,执行 reduce() 函数 
functools.reduce(calculate,[2,1],0)
0
#实际运算过程
(1 - ((2 - 0) * 0)) * ((2 - 0) * 0)
0

#initializer 为3,以及直接将 initializer 值作为序列的第一个元素,结果一致 
functools.reduce(calculate,[3,2,1])
-12
functools.reduce(calculate,[2,1],3)
-12
#实际运算过程
(1 - ((2 - 3) * 3)) * ((2 - 3) * 3)
-12

3. 偏函数:partial()

偏函数是对指定函数的二次包装,通常是将现有函数的部分参数预先绑定,从而得到一个新的函数,该函数就称为偏函数。相比原函数,偏函数具有较少的可变参数,降低了函数调用难度。 偏函数会 “冻结” 一部分函数参数,从而得到一个具有简化操作的新对象。

partial() 函数格式如下:

functools.partial(func, /, *args, **keywords)  

partial() 函数返回一个偏函数,当被调用时,其行为类似于 func 函数附带位置参数 args 和关键字参数 keywords 被调用。  

偏函数被调用的时候,如果提供更多的参数,它们会被附加到 func 函数的位置参数 args 中;如果额外的关键字参数,它们会扩展并重载 func 函数的关键字参数 keywords。  

实现原理大致等价于:  

def partial(func, /, *args, **keywords):
    def newfunc(*fargs, **fkeywords):
        newkeywords = {**keywords, **fkeywords}
        return func(*args, *fargs, **newkeywords)
    newfunc.func = func
    newfunc.args = args
    newfunc.keywords = keywords
    return newfunc

已被 partial() 录入的位置参数、关键字参数,不可被生成的偏函数重复录入,否则报错;

实战示例如下:

#作为参数输入的函数
def functest(var1,var2,var3,var4):
...     return var1 + var2 + var3 + var4
... 
#partial() 生成一个已定义位置参数1、2,和关键字参数var4=5的,functest函数的偏参数s
s = functools.partial(functest,1,2,var4=5)

#偏函数 s 输入位置参数3,执行成功
s(3)
11
#实际运算过程
1 + 2 + 3 + 5
11

#偏函数 s 输入关键字参数var3=6,执行成功
s(var3=6)
14
#实际运算过程
1 + 2 + 6 + 5
14

#输入过多、过少的位置参数,报错
s(3,4)
Traceback (most recent call last):
  File "<input>", line 1, in <module>
TypeError: functest() got multiple values for argument 'var4'

s()
Traceback (most recent call last):
  File "<input>", line 1, in <module>
TypeError: functest() missing 1 required positional argument: 'var3'

#输入已被赋值的关键字参数,报错
s(var2=2,var3=6)
Traceback (most recent call last):
  File "<input>", line 1, in <module>
TypeError: functest() got multiple values for argument 'var2'

4. 函数装饰器:@wraps

使用装饰器包装函数后,被包装的函数属性无法被读取,可以通过@wraps将被包装函数的属性赋予装饰器,方便后续自省操作。

@wraps 函数格式如下:

@functools.wraps(wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)

这是一个便捷函数,用于在定义包装函数时发起调用 update_wrapper() 作为函数装饰器。它等价于 partial(update_wrapper, wrapped=wrapped, assigned=assigned, updated=updated)。 

update_wrapper 函数格式如下:

functools.update_wrapper(wrapper, wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)

更新一个包装函数以使其看起来像是被包装的函数,可选参数是元组,用于指定原始函数的哪些属性直接分配给包装函数上的对应属性,以及使用原始函数中的相应属性更新包装函数的哪些属性。这些参数的默认值是模块级的常量 WRAPPER_ASSIGNMENTS(其中分配给包装函数的 __module__、__name__、__qualname__、__annotations__ 和 __doc__)和 WRAPPER_UPDATES(其更新的包装函数的 __dict__,即实例字典)。  

为了允许访问原始函数以进行内省和其他目的(例如绕过缓存装饰器等 lru_cache()),此函数会自动 __wrapped__ 向包装器添加一个属性,该属性引用被包装的函数。  

未使用@wraps,读取装饰器各属性代码如下:

def log(funx):
    def closure_test(var1,var2):
        print(f'开始函数{funx.__name__}调用!')
        funx(var1,var2)
        print(f'结束函数{funx.__name__}调用!')
    return closure_test

@log
def functiontest(var1: str, var2: list[int]) -> str:
    """
    函数测试
    :param var1: 初始字符串
    :param var2: 整型列表
    :return: 整合后的列表
    """
    for i in var2:
        var1 += str(i)
    print(f'最终结果为{var1}')
    return var1

functiontest('1',(2,3,4,5))

print(functiontest.__name__)
print(functiontest.__doc__)
print(functiontest.__annotations__)

结果为返回闭包函数属性:

开始函数functiontest调用!
最终结果为12345
结束函数functiontest调用!
closure_test
None
{}

使用@wraps,读取装饰器各属性代码如下:

import functools

def log(funx):
    @functools.wraps(funx)
    def closure_test(var1,var2):
        print(f'开始函数{funx.__name__}调用!')
        funx(var1,var2)
        print(f'结束函数{funx.__name__}调用!')
    return closure_test

@log
def functiontest(var1: str, var2: list[int]) -> str:
    """
    函数测试
    :param var1: 初始字符串
    :param var2: 整型列表
    :return: 整合后的列表
    """
    for i in var2:
        var1 += str(i)
    print(f'最终结果为{var1}')
    return var1

functiontest('1',(2,3,4,5))

print(functiontest.__name__)
print(functiontest.__doc__)
print(functiontest.__annotations__)

结果为返回被装饰函数属性:

开始函数functiontest调用!
最终结果为12345
结束函数functiontest调用!
functiontest

    函数测试
    :param var1: 初始字符串
    :param var2: 整型列表
    :return: 整合后的列表
    
{'var1': <class 'str'>, 'var2': list[int], 'return': <class 'str'>}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

燃烧的火鸟啊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值