函数
7.1 闭包
如果在一个内部函数中对外部函数作用域(非全局作用域)的变量进行引用,那么内部函数就会被称为闭包。满足闭包条件:
- 存在于嵌套关系的函数中
- 嵌套在内部函数引用了外部函数的变量
- 嵌套的外部函数会将内部函数名作为返回值返回
# 外部函数
def outer(start = 0):
count = [start] # 函数内的变量
def inner():
count[0] += 1 # 引用外部函数的变量
return count[0]
# 返回内部函数的名称
return inner
quote = outer(5)
print(quote()) # 引用 quote() 函数实际上调用 inner() 函数
- 调用
outer
函数,将 5 传给 start 参数 - count 存放 5
- 返回内部函数
inner
- 将调用
outer
函数返回的 inner 赋值给 quote 变量,即 quote = inner 。此时,quote 变量引用的是inner
函数占用的内存空间。 - 调用 quote 引用的函数 相当于调用
inner
函数。此时,程序来到inner
函数的定义部分 - 将 coute + 1 计算结果再次赋值为列表的元素,此时列表中存放 6
- 返回 count 列表的元素值 6
从内部函数的定义看,在 count
函数执行结束后,变量 count 已被销毁。当 outer
函数执行完后,又会执行内部的 inner
函数,由于 inner
函数使用了 count 变量,此时程序为啥没报错?
主要是函数的闭包会记得外层函数的作用域,在 inner
函数(闭包)中引用了外部函数的 count 变量,所以程序不会释放这个变量。
7.2 装饰器
7.2.1 What
装饰器可以在不改动其他函数的前提下,对函数的功能进行扩充。用于场景:
- 引入日志
- 函数执行的时间统计
- 执行函数前准备处理
- 执行函数后清理功能
- 权限校验
- 缓存
def print_log(func):
print('函数正在运行中')
func()
def func():
print('test')
print_log(test)
定义一个打印日志函数虽然实现了该功能,但破坏了原有代码的逻辑结构。如果要求实现的函数不能修改,只能扩展。即遵循“封闭开放”原则。
装饰器即可满足此条件。以 @
开头
def wrap(func):
print("正在装饰")
def inner():
print('正在验证权限')
func
return inner
@wrap
def test():
print('test')
test()
-
执行
test()
时,发现该函数上面有装饰器 @wrap 会先执行 @wrap 等价于 test = wrap(test)- 执行 wrap(test) ,将函数名 test 作为参数传给 wrap 。在调用 wrap 函数时,会先执行 print 语句,输出 “正在装饰”, 然后会将形参 func 指向 test() 函数体,并将 inner函数 的引用返回给 wrap(test) 作为 wrap(test) 的返回值
-
将 wrap(test) 的返回值赋给 test ,此时 test 指向 inner() 函数,完成了函数test() 的装饰
-
调用 test() 指向的函数。因为 test 指向 inner()函数 ,所以此时,调用 test()函数 相当于调用 inner()函数,输出过程如下:
- 输出 print 语句 “正在验证权限”
- 调用 func 指向的函数,输出 “test”
7.2.2 多个装饰器
多个装饰器可以应用在一个函数中,调用顺序自下而上。
def wrap_one(func):
print("--正在装饰器1--")
def inner():
print('--正在验证权限1--')
func()
return inner
def wrap_two(func):
print("--正在装饰器2--")
def inner():
print('--正在验证权限2--')
func()
return inner
@wrap_one
@wrap_two
def test():
print("---test---")
# 调用 test 之前,已经装饰过了
test()
- 程序将
wrap_one
wrap_two
test
函数加载到内存中
-
test()
函数有两个装饰器,首先执行@wrap_two
-
执行
@wrap_one
也就是test = wrap_one(test)
,执行完毕后会输出:--正在装饰2-- --正在装饰1--
-
装饰完毕后,如果使用
test()
语句调用 test 函数,程序按照从上向下的顺序执行输出:--正在验证权限1-- --正在验证权限2-- --test--
7.2.3 装饰器对有参数函数进行装饰
有参
def wrap(func):
# inner 传参
def inner(a, b):
print("开始验证权限")
# func 传参
func(a, b)
return inner
@wrap
def test(a, b):
print('a = %d, b = %d' %(a, b))
test(1, 2)
开始验证权限
a = 1, b = 2
不定长参数
def wrap(func):
def inner(*args, **kwargs):
print("开始验证权限")
func(*args, **kwargs)
return inner
@wrap
def test(*args, **kwargs):
print('--test--')
test(1, 2,3)
test(a = 1, b = 2,c = 3)
开始验证权限
--test--
开始验证权限
--test--
7.2.4 装饰器对带有返回值的函数进行装饰
def func(function_name):
def func_in():
return function_name()
return func_in
@func
def test():
return 'itheima'
result = test()
print(result)
7.2.5 带有参数的装饰器
如果我们给装饰器添加参数,那么需要增加一层封装,先传参,然后在传递函数名。
def func_arg(args):
def func(function_name):
def func_in():
print('--记录日志-args = %s'%args)
function_name()
return func_in
return func
@func_arg('Link')
def test():
print('--test--')
test()
--记录日志-args = Link
--test--
@func_arg('Link') 等价于 test = func_arg('Link')(test)
由于函数 func_args
的返回值是 func
函数的引用,也就是函数名 func
,等价于 test = func(test)
相比无参的装饰器,带有参的装饰器只是用来“加强装饰”的,如果希望装饰器可以根据参数的不同,对不同的函数进行不同的装饰,可以优先使用带参的装饰器
7.3 常见 Python 内置函数
7.3.1 map 函数
map 函数会根据提供的函数对指定的序列做映射
map(function, iterable, ...)
-
function: 函数名
-
iterablr: 可以是序列、支持迭代的容器或迭代器。
-
调用
map
时,iterable
中的每一个元素都会调用function
函数,所有元素调用function
函数返回的结果会保存到一个迭代器对象中。 -
可以通过
list
函数进行转换成 列表 也可通过for
循环直接遍历迭代器对象,可以取出其内部的每个元素。func = lambda x:x+2 result = map(func, [1, 2, 3, 4, 5]) print(list(result))
-
如果 map 函数传入的
function
带有俩参数,map
也需要传递两个序列result = map(lambda x, y: x+y, [1,2,3],[4,5,6]) print(list(result)) # 结果为 [5, 7, 9]
7.3.2 filter 函数
filter: 会对指定序列执行过滤操作
filter(function, iterable)
- function 可以是函数名,也可以是None。函数只能接收一个参数,返回值为布尔值。
- **iterablr : ** 可以是序列、支持迭代的容器或迭代器。返回值位迭代器对象。
filter 函数的作用是以参数迭代器中的每个元素分别调用 function 函数,最后返回的迭代器包含调用结果为 True
func = lambda x : x & 1
res = filter(func, [1, 2, 3, 4, 5])
print(list(res))
# 结果为
[1, 3, 5]
定义一个匿名函数 func, 返回某个数取余以后的结果,接着调用 filter
函数将**[1, 2, 3, 4, 5]** 中的每个元素取出来,作为 func 的参数调用 func函数 之后转换成列表的形式显示。
序列把原始序列的元素执行取余操作后,得到不能被 2 整除(结果为 True)的元素筛选出来构成新的列表。
7.3.3 reduce 函数
reduce 函数对参数迭代器中的元素进行累积。
functools,reduce(function, iterable[, initializer])
- function: 是带有两个参数的函数
- iterable: 迭代器对象
- initializer: 表示固定的初始值
reduce函数 会依次从迭代器对象中取出每一个元素,和上次调用 function 的结果作为参数再次调用 function函数。
reduce 函数被放置在 functools 模块中,使用时先导入
from functools import reduce
func = lambda x, y: x + y
res = reduce(func, [1,2,3,4,5])
print(res)
# 结果为
15
调用 reduce 函数是传入了 func 和 序列, 所以程序将列表的每个元素取出来和上次调用后的结果作为参数再次调用 func 函数,最后返回给 res.
iterable 参数也可以传入字符串类型.function 不能为None.
ret = reduce(lambda x, y: x+y, ['aa', 'bb', 'cc'], 'dd')
print(ret)
# 结果为
ddaabbcc
调用 reduce 函数是传入了 func 和 序列, 所以程序将列表的每个元素取出来和上次调用后的结果作为参数再次调用 func 函数,最后返回给 res.
iterable 参数也可以传入字符串类型.function 不能为None.
ret = reduce(lambda x, y: x+y, ['aa', 'bb', 'cc'], 'dd')
print(ret)
# 结果为
ddaabbcc