Python中的高阶函数以及柯里化、functools模块、lru_cache实现

1. 高阶函数和柯理化

1.1. 高阶函数(High-Order Function)

一个函数要成为高阶函数,需要满足下面至少一个条件:

  1. 函数定义中接收一个或者多个函数作为参数
  2. 返回值为函数

满足上述两点条件的任意一点,即可将该函数称为高阶函数。

Python中内建了一些高阶函数,比如sorted,比如min这类可以接收函数作为参数的内建函数;另外,比如偏函数functools.partial,这个函数接收函数作为参数,同时将函数作为返回值。函数装饰器通常都属于高阶函数,装饰器既需要函数作为参数,也需要将函数作为返回值。

下面实现一个自定义的高阶函数。具体代码如下所示:

def echo_hw(arg):
    print('Hello World! This is {}'.format(arg))
    
def high_order_func(func, arg):
    func(arg)
    print('Welcome to Python!')

high_order_func(echo_hw, 'Python')  

上述代码的输出结果如下所示:

Hello World! This is Python
Welcome to Python!

上述代码并没有什么实际意义,只是一种定义高阶函数的方式——接收函数作为参数的函数,可以被称为是高阶函数。

将上述代码稍作改变,使其返回值为参数——也可以被称为是高阶函数。具体代码如下所示:

def high_order_func(arg):
    def echo_hw():
        print('Hello World! This is {}'.format(new_arg))
    new_arg = arg + '\nWelcome to Python!'
    return echo_hw

ret_func = high_order_func('Python')
ret_func()

上述函数的输出结果如下所示:

Hello World! This is Python
Welcome to Python!

上述函数代码同样没有什么实际意义,只是说明如果一个函数的返回值是函数,那么这个函数也可以被称为是高阶函数。

上面的两种代码实现方式虽然有差异,但是实现的目的是相同的,且都符合高阶函数的构成条件,所以都是高阶函数。

1.2. 柯里化(Currying)

而所谓的柯里化(Currying),则是指,将接收多个参数的函数转换为单参函数,并将剩余的参数封装在该单参函数内的嵌套函数中,作为嵌套函数的参数(嵌套函数可以接收所有剩余的参数,也可以接收剩余参数中的一个或者几个,并将此后剩余的参数再继续构建嵌套函数)。通过嵌套函数以及闭包作用域,将多参函数的参数拆解到多个嵌套函数中。柯里化的本质是嵌套函数以及闭包作用域下的变量查询。柯里化是实现函数装饰器的一个手段。

下面通过示例,说明如何将一个普通函数进行柯里化改稿的过程。

一个多参普通函数,如下所示:

def multi_add(a, b, c):
 return a + b + c

res = multi_add(1, 2, 3)
print(res)

上述函数的输出结果为6。

将上述普通函数通过柯里化改造为func(a)(b, c)的形式。

改造代码具体如下所示:

def multi_add_cury(a):
 def wrapper(b, c):
     return a + b + c
 return wrapper

ret_func = multi_add_cury(5)
res = ret_func(6, 7)
print(res)

上述即为柯里化之后的函数,需要进行两次函数调用(伴随着两次参数传递过程)才能获得最终的执行结果,其执行结果为18。

将上述普通函数通过柯里化改造为func(a)(b)(c)的形式。

改造代码具体如下所示:

def multi_add_cury1(a):
 def wrapper(b):
     def inner(c):
         return a + b + c
     return inner
 return wrapper

wrap_func = multi_add_cury1(7)
inner_fun = wrap_func(8)
res = inner_fun(9)
print(res)

上述即为柯里化之后的函数,需要进行三次函数调用(伴随着三次参数传递过程)才能获得最终的执行结果。其计算结果为24。

最后还可以将普通函数通过柯里化改造为func(a, b)(c)的形式。

改造代码具体如下所示:

def multi_add_cury2(a, b):
 def wrapper(c):
     return a + b + c
 return wrapper

wrap_func = multi_add_cury2(11, 12)
res = wrap_func(13)
print(res)

上述即为柯里化之后的函数,需要经过两次函数调用(伴随两次参数传递过程)才能获得最终的计算结果。上述执行结果为36。

上述即为函数柯里化的过程以及方式。

2. functools包内的每个函数的功能作用

functools模块中提供了一些高阶函数,用于实现用户自己的通用函数。在我安装的Python-3.7中,包含的方法个数为9个,分别如下所示:

  1. functools.cmp_to_key:这个高阶函数的作用是将老式的比较函数转换为新式的key形式函数,以便其可以用在接收key=key_func作为参数的函数(比如sorted(), min(), max(), heapq.nlargest()heapq.nsmallest(), itertools.groupby()等等这类函数)中。这类函数中的key参数通常要求指定的函数接收1个参数,所以就需要这个cmp_to_key高阶函数将传统的接受2个参数的比较函数转换为接受1个参数的key形式函数。所谓的比较函数,是指接收2个参数,并比较这两个参数的大小,如果是小于的关系,则返回负数;如果相等,则返回0;如果是大于关系,则返回正数。而key形式函数是一个可调用对象,其接受1个参数,并且返回另一个对象,使用这个返回的对象作为key=的参数值。

    这个函数的帮助基本信息如下所示:

    functools.cmp_to_key(func)
    

    下面通过sorted函数中的key=参数,示例这个functools.cmp_to_key高阶函数的使用方式。具体如下所示:

    from functools import lru_cache, cmp_to_key
    
    res = sorted([7, 2, 3, 1, 5, 8], key=cmp_to_key(lambda x, y: x - y))
    print(res)
    

    上述函数的执行结果如下所示:

    [1, 2, 3, 5, 7, 8]
    
    Process finished with exit code 0
    

    通过上述函数就完成了参数的转换。但是这个背后过程并不很清楚。

    下面再看两个关于sorted函数中不同的key=参数取值情况的排序表现。

    sorted函数的key=参数不适用functools.cmp_to_key高阶函数的时候,其排序表现如下所示:

    sorted([7, 3, 2, 1, 5, 6, 9, 8], key=lambda x: x)
    

    上述语句的执行结果如下所示:

    [1, 2, 3, 5, 6, 7, 8, 9]
    

    从上述结果中可以看出,对结果进行了排序,其实省略掉key=这个参数,也是默认按照升序排列的。接下来对key=的参数稍作调整,具体如下所示:

    sorted([7, 3, 2, 1, 5, 6, 9, 8], key=lambda x, y: x, y)
    

    此时执行报错,具体如下所示:

    File "C:\Users\ikkys\AppData\Local\Temp/ipykernel_187720/3200397783.py", line 1
     sorted([7, 3, 2, 1, 5, 6, 9, 8], key=lambda x, y: x, y)
                                                         ^
    SyntaxError: positional argument follows keyword argument
    

    上面的异常信息中提示,在关键字参数后面跟了一个位置参数。因为key参数要求指定的函数为单参函数,所以这里指定两个是不合适的。

    接下来使用functools.cmp_to_key这个高阶函数将接收两个参数的比较函数转换为单参函数。

    具体如下所示:

    sorted([7, 3, 2, 1, 5, 6, 9, 8], key=cmp_to_key(lambda x: x))
    

    上述的执行结果会报错,具体如下所示:

    ---------------------------------------------------------------------------
    TypeError                                 Traceback (most recent call last)
    ~\AppData\Local\Temp/ipykernel_187720/3510372626.py in <module>
    ----> 1 sorted([7, 3, 2, 1, 5, 6, 9, 8]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值