闭包的构成条件:

  1. 在函数嵌套(函数里面再定义函数)的前提下
  2. 内部函数使用了外部函数的变量(还包括外部函数的参数)
  3. 外部函数返回了内部函数

简单闭包

1
2
3
4
5
6
7
8
9
10
11
12
13
def fun_out(num1):
# 定义外部函数
def fun_inner(num2):
# 内部函数使用了外部函数的变量
result = num2 + num1
print("结果是: ", result)
# 外部函数返回了内部函数,这里返回的内部函数就是闭包
return fun_inner

f = fun_out(1) # 创建闭包实例

f(2)
f(3)
  1. 当返回的内部函数使用了外部函数的变量就形成了闭包
  2. 闭包可以对外部函数的变量进行保存

闭包的作用

*闭包的作用: 闭包不仅可以保存外部函数的变量还可以提高代码的可重用性(不需要再手动定义额外的功能函数*

1
2
3
4
5
6
7
8
9
10
11
12
13
def config_name(name):  # 定义外部函数
# 内部函数使用了外部函数的变量
def say_info(info):
print(name + ":" + info)

return say_info

tom = config_name("Tom")
tom("你好!")
tom("你好,在吗?")

jerry = config_name("jerry")
jerry("不在,不和你玩!")

装饰器的功能特点:

  1. 不修改已有函数的源代码
  2. 不修改已有函数的调用方式
  3. 给已有函数增加额外的功能
1
2
3
4
5
6
7
8
9
10
11
12
13
14
def check(fn):
# 追加一个登陆验证的功能
def inner():
print("请先登陆!")
fn()
return inner


def comment():
print("发表评论!")


comment = check(comment)
comment()
  • 闭包函数有且只有一个参数,必须是函数类型,这样定义的函数才是装饰器。
  • 写代码要遵循开放封闭原则,它规定已经实现的功能代码不允许被修改,但可以被扩展。

装饰器的语法糖写法

如果有多个函数都需要添加登录验证的功能,每次都需要编写func = check(func)这样代码对已有函数进行装饰,这种做法还是比较麻烦。

Python给提供了一个装饰函数更加简单的写法,那就是语法糖,语法糖的书写格式是: @装饰器名字,通过语法糖的方式也可以完成对已有函数的装饰

糖写法演示

1
2
3
@check
def comment():
print("发表评论")

@check ==> comment = check(comment)

使用装饰器添加登陆验证功能

1
2
3
4
5
6
7
8
9
10
11
12
def check(fn):
print("装饰函数执行了")
def inner():
print("请登陆....")
fn()
return inner

@check # @check 等价于 comment = check(comment)
def comment(): # 装饰器的执行时间是加载模块时立即执行。
print("发表评论!")

comment() # 调用函数

使用装饰器对函数执行时间进行统计

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import time

def get_time(func):
# 定义函数的嵌套,
def inner():
begin_time = time.time()
func() # 内部函数使用了外部函数的变量包括参数
end_tiem = time.time()
print("函数执行花费了:%f" % (end_tiem-begin_time))
print("测试:",end_tiem-begin_time)
return inner # 外部函数返回了内部函数


@get_time # 等价于test = get_tiem(test)
def test():
for i in range(10000000):
i += 1
print(i)

test()

带有不定长参数的装饰器

*args和**kwargs都是可变长度参数的方式

*args:用于传递不定数量的位置参数,它会将所有的位置参数打包成一个元组(tuple)。

**kwargs:用于传递不定数量的关键字参数,它会将所有的关键字参数打包成一个字典(dictionary)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
def logging(fun):  # 这里fun = sum_num
# *args和**kwargs都是可变长度参数的方式,它们之间的区别如下:
#
# *args:用于传递不定数量的位置参数,它会将所有的位置参数打包成一个元组(tuple)。
#
# **kwargs:用于传递不定数量的关键字参数,它会将所有的关键字参数打包成一个字典(dictionary)。
def inner(*args, **kwargs):
print("--正在努力计算--")
fun(*args, **kwargs) # 调用sum_num
return inner

@logging # 这里sum_num = logging(sum_num)
# 执行到这里就有 sum_num = inner
def sum_num(*args, **kwargs):
result = 0
for vaules in args: # args元组形式
result += vaules

for vaules in kwargs.values(): # kwargs将所有参数转化为字典类型的数据
result += vaules

print(result)

sum_num(1, 2, a=10)

通用装饰器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 通用装饰器
def logging(fn): # 2,将函数sum_num 作为参数传递
def inner(*args, **kwargs):
print("--正在努力计算--")
result = fn(*args, **kwargs) # fn = sum_num, fn = subtraction
return result

return inner


# 使用语法糖装饰函数
@logging # 1 这里sum_num = logging(sum_num) -- 之后sum_num = inner,调用sum_num相当于调用inner
def sum_num(*args, **kwargs):
result = 0
for value in args:
result += value

for value in kwargs.values():
result += value

return result


@logging # 3,这里subtraction = logging(subtraction) 之后subtraction = inner,调用subtraction相当于调用inner
def subtraction(a, b):
result = a - b
print(result)

result = sum_num(1, 2, a=10) # 调用sum_num 函数即调用inner函数,其中fun()为sum_num()本体
print(result)

subtraction(4, 2)