闭包
函数
函数名的作用
- 函数名存放的是函数所在内存空间的地址(即:函数名代表函数入口地址)
- 函数名()执行函数名所存放空间地址中的代码(即:函数名()代表函数调用:直接调用)
- 函数名可以像普通变量一样赋值,赋值后的结果与原函数名作用是一样的(即:函数名可以做函数参数,函数名做函数参数是研究的重点)
函数作为对象,可以赋值
# 定义无返回值函数
def fun01():
print("hello world")
print(fun01) # 打印函数的地址值
print(fun01()) # 先调用函数,然后再打印fun01函数中的return内容;由于此函数无返回值,故打印None
f2 = fun01 # 相当于把fun01的地址赋值给了f2
print(f2) # 结果与print(fun01)一样
print(f2) # 结果与print(fun01())一样
函数作为(实际)参数使用,可以传入函数
函数名作为实参传递,本质上传递的是对应函数的地址。
# 1.定义普通函数method(),输出内容
def method():
print("---我是 method 函数")
# 2.定义func()函数,接收1个参数(函数对象),然后调用 该函数即可
def func(fn_name):
'''
该函数用于接收1个函数对象,然后再内部 调用此函数
:param fn_name:接收到的函数名,充当:实参(对象)
:return:无
'''
fn_name
闭包
概念与作用
闭包可以保存函数内的变量,而不会随着调用完函数而被销毁。
在函数嵌套的前提下,内部函数使用了外部函数的变量,并且外部函数返回了内部函数,这种:使用外部函数变量的内部函数称为闭包
构成条件
- 1.有嵌套:在函数嵌套(函数里面再定义函数)的前提下;
- 2.有引用:内部函数使用了外部函数的变量(还包括外部函数的参数);
- 3.有返回:外部函数返回了内部函数名。
定义一个用于求和的闭包
其中,外部函数有参数num1,内部函数有参数num2,然后调用,并用于求解两数之和
# 闭包的构成条件:
# 1.在函数嵌套(函数里面再定义函数)的前提下
def func_out(num1):
def func_inner(num2):
# 2.内部函数使用了外部函数的变量(还包括外部函数的参数)
num = num1 + num2
print("现在的值:",num)
#3.外部函数返回了内部函数名
return func_inner
# 创建闭包实例
f = func_out(10) # f = func_out函数的返回值 = inner这个内部函数对象 这行代码走完,外部函数就执行结束了
执行闭包
f(1)
nonlocal关键字
这是1个关键字,可以实现 让内部函数 去修改 外部函数的变量值。
global:声明变量 为全局变量
nonlocal:声明 外部函数的变量值 可以被 内部函数修改
# 需求: 编写1个闭包, 让内部函数去访问外部函数的参数a = 100, 并观察效果.
# 1. 闭包写法, 实现上述需求.
def fun_outer(): # 有嵌套
a = 100 # 外部函数的 变量
# 定义内部函数, 去访问, 并修改外部函数的变量.
def fun_inner(): # 有嵌套
# 核心细节: 在内部函数中修改外部函数的变量值, 要通过 nonlocal关键字实现.
nonlocal a
a = a + 1
print(f'a的值为: {a}') # 有引用
return fun_inner # 有返回
# 2. 调用函数.
# 2.1 调用外部函数, 获取其返回值, 即: 内部函数对象
fn = fun_outer() # fn = fun_inner
# 2.2 调用"内部函数"
fn() # 101
fn() # 102
fn() # 103
装饰器
装饰器的作用
装饰器的作用是:不改变原有函数的基础上,给原有函数增加额外功能
装饰器本质上就是一个闭包函数
构成条件
- 1.有嵌套:在函数嵌套(函数里面再定义函数)的前提下;
- 2.有引用:内部函数使用了外部函数的变量(还包括外部函数的参数);
- 3.有返回:外部函数返回了内部函数名;
- 4.有额外功能:给需要装饰的原有函数增加额外功能 。
语法
方式一:传统方法: 变量名 = 装饰器名(原有函数名)
变量名() # 执行的就是 装饰后的 原函数
方式二:语法糖(语法格式简化版):@装饰器名 (这种方式最常见也最常用)
细节:
内部函数的形式,必须和 原函数(要被装饰的函数)形式一致,即:要有参数都有参数,要有返回值就都有返回值
装饰不定长函数
"""
细节:
装饰器的内部函数 格式必须和 (要被装饰的)原函数 格式保持一致, 即: 要么都是无参无返回, 要么都是无参有返回......
"""
# 案例: 演示装饰器 装饰 有参(可变类型)有返回值的函数.
# 需求: 定义函数 get_sum(), 用于计算多个数据和. 在不改变其源码的情况下, 实现: 添加一个友好提示.
# 1. 编写装饰器.
def print_info(fn): # 有嵌套
# 定义内部函数, 其格式必须和 原函数 格式保持一致.
def inner(*args, **kwargs): # 有嵌套
print('[友好提示] 正在努力计算中!') # 有额外功能
return fn(*args, **kwargs)
return inner # 有返回
# 2. 编写原函数: 有参(可变参数)有返回值.
@print_info
def get_sum(*args, **kwargs):
"""
该函数用于计算 多个数据的 和.
:param args: 可变参数, 会接收所有的位置参数, 然后放到元组中
:param kwargs: 可变参数, 会接收所有的关键字参数, 然后放到字典中
:return: 多个数据的求和 结果.
"""
# 2.1 定义sum变量, 记录求和结果.
sum = 0
# 2.2 先计算所有位置参数(元组)的和. 例如: (1, 2, 3, 4, 5)
for i in args:
sum += i
# 2.3 再计算所有关键字参数(字典)的和. 例如: {'a': 10, 'b': 20, 'c': 30}
for value in kwargs.values():
sum += value
# 2.4 返回值最终的计算结果.
return sum
# 3. 在main函数中进行测试.
if __name__ == '__main__':
print(get_sum(1, 2, 3, 4, 5, a = 10, b = 20, c = 30))
多个装饰器装饰一个函数
多个装饰器的装饰过程是:离函数最近的装饰器先装饰,然后外面的装饰器再进行装饰,由内到外的装饰过程
细节:
- 1.多个装饰器,装饰1个函数,装饰的顺序是 由内向外的 即:传统写法
- 2.但是多个装饰器的执行顺序是,由上往下的 即:语法糖写法
"""
细节(记忆):
1. 多装饰器装饰1个原函数, 会按照由内到外的顺序进行装饰(即: 传统装饰写法).
2. 但是你看到的执行顺序是: 从上往下(从外到内)的顺序执行的. (语法糖写法)
"""
# 需求: 发表评论前, 需要先登录, 再进行验证码验证.
# 1. 定义装饰器, 负责给原函数增加: 登陆功能.
def check_login(fn):
def inner(): # 有嵌套
print('登录中...') # 有额外功能
fn() # 有引用
return inner # 有返回
# 2. 定义装饰器, 负责给原函数增加: 验证码功能.
def check_code(fn):
def inner(): # 有嵌套
print('校验验证码...') # 有额外功能
fn() # 有引用
return inner # 有返回
# 3. 定义原函数.
# @check_login
# @check_code
def comment():
print('发表评论!')
# 4. 测试
if __name__ == '__main__':
# 方式1: 传统方式.
# comment = check_code(comment) # 增加验证码功能.
# comment = check_login(comment) # 增加登陆功能
# comment()
cc = check_code(comment) # 增加验证码功能.
cl = check_login(cc) # 增加登陆功能
cl()
# 方式2: 装饰器语法糖写法.
# comment()
带参数的装饰器
一个装饰器 装饰 多个原函数
使用带有参数的装饰器,其实是在装饰器外面又包裹了一个函数,使用该函数接收参数,返回装饰器。
细节:
1个装饰器 的参数, 只能有 1个
例如:需求:定义1个既能装饰 减法运算,又能装饰 加法运算 的装饰器
思路1:
# 1.定义装饰器,实现:传入add()函数,就提示 加法运算,传入substract()函数,就提示:减法运算
def logging(flag):
def print_info(fn_name): #这个才是我们的装饰器,因为1个装饰器的参数只能有1个,所以在其外边再包裹一层,专门用于传入:参数
def inner(a,b): # 有嵌套
if flag == '+':
print('正在努力计算 加法 中...') # 有额外功能
elif flag == '-':
print('正在努力计算中...') # 有额外功能
fn_name(a,b) #有引用
return inner #有返回
return print_info
# 2.定义函数add(),表示:加法运算
@logging('+')
def add(a,b):
result = a + b
print(result)
# 3.定义函数substract(),表示:减法运算
@logging('-')
def substract(a,b):
result = a - b
print(result)
# 在main函数中测试
if __name__=='__main__':
# 4.调用add()函数,实现:计算两个整数和
add(11,33)
print('-' * 20)
# 5.调用substract(),实现:计算两个整数差
substract(100,20)
思路2:为了做题,即:实现这个需求
# 1.定义装饰器,实现:传入add()函数,就提示 加法运算,传入substract()函数,就提示:减法运算
def print_info(fn_name):
def inner(a,b): # 有嵌套
if fn_name == add:
print('正在努力计算 加法 中...') # 有额外功能
elif fn_name == substract:
print('正在努力计算 减法 中...') # 有额外功能
fn_name(a,b) #有引用
return inner #有返回
# 2.定义函数add(),表示:加法运算
@print_info
def add(a,b):
result = a + b
print(result)
# 3.定义函数substract(),表示:减法运算
@print_info
def substract(a,b):
result = a - b
print(result)
# 在main函数中测试
if __name__=='__main__':
# 4.调用add()函数,实现:计算两个整数和
add(11,33)
print('-' * 20)
# 5.调用substract(),实现:计算两个整数差
substract(100,20)