返回函数
高阶函数除了可以接受函数作为参数外,还可以把函数作为结果值返回
通常情况求和函数
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的作用就是,把一个函数的某些参数给固定住
(也就是设置默认值),返回一个新的函数。