• 完成 Python 教程。
6 函数式编程 (二)
- sorted()函数也是一个高阶函数,它可以接收一个key函数来实现自定义的排序。
- 高阶函数的抽象能力是非常强大的,而且核心代码可以保持得非常简洁。
练习:假设我们用一组tuple表示学生名字和成绩,请用sorted()
对列表分别按名字排序:
# -*- coding: utf-8 -*-
L = [('Bob', 75), ('Adam', 92), ('Bart', 66), ('Lisa', 88)]
# 请用sorted()对上述列表分别按名字排序
def by_name(t):
return t[0].lower()
L2 = sorted(L, key=by_name)
print(L2)
# 再按成绩从高到低排序
def by_score(t):
return -t[1]
L2 = sorted(L, key=by_score)
print(L2)
- 高阶函数除了可以接受函数作为参数外,还可以把函数作为结果值返回。
def lazy_sum(*args):
def sum():
ax = 0
for n in args:
ax = ax + n
return ax
return sum
在这个例子中,我们在函数lazy_sum
中又定义了函数sum
,并且,内部函数sum
可以引用外部函数lazy_sum
的参数和局部变量,当lazy_sum
返回函数sum
时,相关参数和变量都保存在返回的函数中,这种称为“闭包(Closure)”的程序结构拥有极大的威力。
请再注意一点,当我们调用lazy_sum()
时,每次调用都会返回一个新的函数,即使传入相同的参数。
- 注意到返回的函数在气定义内部引用了局部变量args,所以,当一个函数返回了一个函数后,其内部的局部变量还被新函数引用,所以,闭包用起来简单,实现起来可不容易。
- 另一个问题就是,返回的函数没有立刻执行。
- 返回闭包时牢记一点:返回函数不要引用任何循环变量,或者后续会发生变化的变量。
练习:利用闭包返回一个计数器函数,每次调用它返回递增整数:
def createCounter():
n=0
def counter():
nonlocal n
n = n + 1
return n
return counter
- 一个函数可以返回一个计算结果,也可以返回一个函数。
- 返回一个函数时,牢记该函数并未执行,返回函数中不要引用任何可能会变化的变量。
- 关键字lambda表示匿名函数。
- 匿名函数有个限制,就是只能有一个表达式,不用写return,返回值就是该表达式的结果。
- 匿名函数的一个好处是因为函数没有名字,不必担心函数名冲突。此外,匿名函数也是一个函数对象,也可以把匿名函数赋值给一个变量,再利用变量来调用该函数。
- 函数对象都有一个__name__属性,可以拿到函数的名字。
- 现在假设我们要增强一个函数的功能,比如,在函数调用前后自动打印日志,但又不希望修改now()函数的定义,这种在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)。
练习:请设计一个decorator,它可作用于任何函数上,并打印该函数的执行时间:
# -*- coding: utf-8 -*-
import time, functools
def metric(fn):
@functools.wraps(fn)
def wrapper(*args,**kw):
start=time.time()
program=fn(*args,**kw)
end=time.time()
print("%s executed in %s ms"%(fn.__name__,end-start))
return program
return wrapper
# 测试
@metric
def fast(x, y):
time.sleep(0.0012)
return x + y;
@metric
def slow(x, y, z):
time.sleep(0.1234)
return x * y * z;
f = fast(11, 22)
s = slow(11, 22, 33)
if f != 33:
print('测试失败!')
elif s != 7986:
print('测试失败!')
- 在面向对象(OOP)的设计模式中,decorator被称为装饰模式。OOP的装饰模式需要通过继承和组合来实现,而Python除了能支持OOP的decorator外,直接从语法层次支持decorator。Python的decorator可以用函数实现,也可以用类实现。
- decorator可以增强函数的功能,定义起来虽然有点复杂,但使用起来非常灵活和方便。
functools.partial
就是帮助我们创建一个偏函数的,不需要我们自己定义int2()
,可以直接使用下面的代码创建一个新的函数int2。
>>> import functools >>> int2 = functools.partial(int, base=2) >>> int2('1000000') 64 >>> int2('1010101') 85
- 简单总结
functools.partial
的作用就是,把一个函数的某些参数给固定住(也就是设置默认值),返回一个新的函数,调用这个新函数会更简单。 - 当函数的参数个数太多,需要简化时,使用
functools.partial
可以创建一个新的函数,这个新函数可以固定住原函数的部分参数,从而在调用时更简单。