Python 函数式编程(返回、匿名、偏函数)

返回函数

高阶函数除了可以接受函数作为参数外,还可以把函数作为结果值返回

通常情况求和函数

def calc_sum(*args):
    ax = 0
    for n in args:
        ax = ax + n
    return ax

但是,如果不需要立刻求和,而是在后面的代码中,根据需要再计算怎么办?可以不返回求和的结果,而是返回求和的函数:

def lazy_sum(*args):
    def sum():
        ax = 0
        for n in args:
            ax = ax + n
        return ax
    return sum

调用返回求和函数,而不是求和值
>>> f = lazy_sum(1, 3, 5, 7, 9)
>>> f
<function lazy_sum.<locals>.sum at 0x101c6ed90>

调用f()开始计算
>>> f()
25

每次调用都会返回新函数,即使参数相同
>>> f1 = lazy_sum(1, 3, 5, 7, 9)
>>> f2 = lazy_sum(1, 3, 5, 7, 9)
>>> f1==f2
False

闭包:
在这个例子中,我们在函数lazy_sum中又定义了函数sum,并且,内部函数sum可以引用外部函数lazy_sum的参数和局部变量,当lazy_sum返回函数sum时,相关参数和变量都保存在返回的函数中,这种称为“闭包(Closure)”的程序结构拥有极大的威力。

当一个函数返回了一个函数后,其内部的局部变量还被新函数引用
返回的函数并没有立刻执行,而是直到调用了f()才执行
def count():
    # fs里最终存的是循环创建未被调用的三个函数f
    fs = []
    for i in range(1, 4):
        # 每次循环都是在创建函数f但并没有调用
        def f():
            return i * i
        # 此时函数f并没有被调用,添加的是函数f本身,不是f的调用结果
        fs.append(f)
        # 也就是说count()得到的返回值是三个函数
    # 循环结束i为3
    return fs

f1, f2, f3 = count()  #这个赋值操作是将[]三个值分别赋值给f1 f2 f3

>>> f1()
9
>>> f2()
9
>>> f3()
9
#   易错点1
# 运行print(count())你以为会输出[1,4,9],而实际上打印了由三个函数构成的列表
# 如果要想打印[1,4,9],应该将fs.append(f)里的f改为f()
每次循环都返回一个函数,函数引用了变量i,函数并没有被立刻执行,
都返回完后执行函数时,变量已经是3

返回闭包时牢记一点:返回函数不要引用任何循环变量,或者后续会发生变化的变量

如果要引用循环变量,就创建一个函数,让循环变量作为参数,函数立刻执行

匿名函数

传入函数时,有些时候,不需要显式地定义函数,直接传入匿名函数更方便。

关键字lambda表示匿名函数,冒号前面的x表示函数参数。
>>> list(map(lambda x: x * x, [1, 2, 3, 4, 5, 6, 7, 8, 9]))
[1, 4, 9, 16, 25, 36, 49, 64, 81]

匿名函数有个限制,就是只能有一个表达式,
不用写return,返回值就是该表达式的结果。

可以把匿名函数作为返回值返回
def build(x, y):
    return lambda: x * x + y * y

装饰器

传送门
由于函数也是一个对象,而且函数对象可以被赋值给变量,所以,通过变量也能调用该函数。

>>> def now():
...     print('2015-3-25')
...
>>> f = now
>>> f()
2015-3-25

我们要增强函数的功能,但又不希望修改函数的定义,这种在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)。

本质上,decorator就是一个返回函数的高阶函数
def log(func):
    def wrapper(*args, **kw):
        print('call %s():' % func.__name__)
        return func(*args, **kw)
    return wrapper

@log
def now():
    print('2015-3-25')

调用
>>> now()
call now():
2015-3-25

把@log放到now()函数的定义处,相当于执行了语句:
now = log(now)
由于log()是一个decorator,返回一个函数,所以,原来的now()函数仍然存在,
只是现在同名的now变量指向了新的函数,于是调用now()将执行新函数,
即在log()函数中返回的wrapper()函数。
decorator本身需要传入参数
def log(text):
    def decorator(func):
        def wrapper(*args, **kw):
            print('%s %s():' % (text, func.__name__))
            return func(*args, **kw)
        return wrapper
    return decorator

@log('execute')
def now():
    print('2015-3-25')
执行
>>> now()
execute now():
2015-3-25
相当于执行
>>> now = log('execute')(now)
经过decorator装饰之后的函数,它们的__name__发生变化
解决
在定义wrapper()的前面加上@functools.wraps(func)即可
import functools

def log(text):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kw):
            print('%s %s():' % (text, func.__name__))
            return func(*args, **kw)
        return wrapper
    return decorator

偏函数

偏函数是将所要承载的函数作为 partial() 函数的第一个参数,原函数的各个参数依次作为 partial() 函数的后续参数,除非使用关键字参数。

int()函数可以把字符串转换为整数,当仅传入字符串时,
int()函数默认按十进制转换:

>>> int('123466')
123466

修改进制
传入base参数
>>> int('10' ,base =16)
16

如果要大量引用
我们想到,可以定义一个int2()的函数,默认把base=2传进去:
def int2(x, base=2):
    return int(x, base)
这样,我们转换二进制就非常方便了:
>>> int2('1000000')
64

也可以创建一个偏函数0
>>> import functools
>>> int2 = functools.partial( int ,base = 2)
>>> int2('1010')
10

functools.partial的作用就是,把一个函数的某些参数给固定住
(也就是设置默认值),返回一个新的函数。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

王蒟蒻

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

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

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

打赏作者

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

抵扣说明:

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

余额充值