我使用的Python版本为3.7,没有装配环境的同学可以到下方链接查看教程。
本期内容依旧是高阶函数的内容,接上期。
函数闭包
Python允许在函数中定义函数,内部函数无法在外部函数之外被调用,类似Java的内部类。
需要注意的一点是,内部函数调用外部函数的属性时,采用的是引用传值,这可能会带来一些问题:
def func():
res = []
for x in range(1, 4):
def f():
return x**2
res.append(f)
return res
for f in func():
print(f())
# 9
# 9
# 9
没错,也许你觉得输出的应该是1、4、9,但是因为引用传值,在循环的最后x是3,所以它们的调用结果都是9。
这个特性在某些时候十分方便,而且它的麻烦很容易规避,只要避免将值留到以后调用的时候才处理就行。
匿名函数
对许多语言来说,匿名函数都是一个不可或缺的东西,它避免我们为了某些不常用的功能而专门去写一个函数,而是通过一行简洁的代码来完成。
Python中匿名函数的语法:lambda a, b: a if a > b else b
,代码限制在一行之内。以lambda
为起始标识;:
左侧为参数,可以有任意个;右侧为函数的返回值,可以在其中使用变形的if
语句等。
在map
函数上的简单应用:
itr = map(lambda x: x**2, [0, -9, 3])
print(list(itr))
# [0, 81, 9]
函数装饰器
这是一个稍显复杂的概念,主要是其代码比较绕,一般嵌套了多层内部函数,请耐下心来慢慢理解。
函数装饰器,顾名思义,是给函数添加一些新东西。比如在代码调试的过程中,我们需要输出一些调试信息,这时候在每个函数中都添加上一行代码显得十分繁琐,因为之后还要删除,装饰器就是为了解决这个烦恼。
基础装饰器
看以下代码,展示了一个最简单的装饰器:
def func(x):
return x*x
def deco(f):
def f2(x):
print('call ' + f.__name__ + '()')
return f(x)
return f2
func = deco(func)
print(func(5))
# call func()
# 25
上述代码成功让func函数输出了调试信息,而没有更改func的代码。这里要注意的一点是,若其中的代码func = deco(func)
改为f = deco(func)
,调用输出f()
依旧是call func()
,因为传入的函数为func
,其相关属性并没有改变。
@语法
以上展示的是装饰器的初级应用,因为func = deco(func)
这行代码与函数的定义分离了,不利于我们理解上下文,通过@
语法我们可以在函数定义的时候就知道它添加的那些功能:
@deco
def func2(x):
return x*x
print(func2(3))
# call func2()
# 9
参数传递
使用函数装饰器还有一个传递参数的问题,由于函数的参数个数不是固定的,在通过函数装饰器装饰之后,可能无法正常调用参数,所以在装饰器内传递参数时应统一传递两个参数:*args, **kw
。
带参数的装饰器
此处不必多讲,直接看代码就行:
import functools
from functools import reduce
def performance(words):
def perf_decorator(f):
def wrapper(*args, **kw):
print('call %s() as %s' % (f.__name__, words))
return f(*args, **kw)
return wrapper
return perf_decorator
@performance('test')
def factorial(n):
return reduce(lambda x,y: x*y, range(1, n+1))
print(factorial(12))
# call factorial() as test
# 479001600
因为使用了传递参数的装饰器,所以需要多嵌套一层函数来传递参数。
偏参数
偏函数改变了原函数的调用方法。对于一些函数,经常会传递一些固定的参数,在不更改这些函数的源码的情况下,我们可以使用高阶函数来进行包装:
def int2(x, base=2):
return int(x, base)
print(int2('10010'))
# 18
我们也可以采用functools
中提供的方法:
import functools
int2 = functools.partial(int, base=2)
print(int2('10010'))
# 18