‘闭包’, '装饰器’及其应用场景
一, 闭包及其应用场景
图解
闭包的定义
概述: 内部函数 使用了 外部函数 的变量, 这种写法就称之为闭包.
格式:
def 外部函数名(形参列表):
外部函数的(局部)变量
def 内部函数名(形参列表):
内部函数的(局部)变量
return 内部函数名
前提条件:
- 有嵌套. 外部函数嵌套内部函数
- 有引用. 内部函数引用外部函数的变量
- 有返回. 外部函数中, 返回 内部函数名(对象)
细节:
函数名 和 函数名() 是两个概念, 前者表示 函数对象 , 后者表示 调用函数, 获取返回值 .
案例
案例(1): 观察函数名和函数名()
案例需求
创建函数, 分别打印函数名和函数名()
证明: 函数名 -> 是对象, 函数名() -> 是调用函数, 获取返回值
实现思路
- 创建函数.
- 打印函数名.
- 打印函数名().
- 将函数名赋值给变量, 这个变量就是: 函数对象.
- 打印函数对象.
- 打印函数对象().
# 1. 创建函数.
def get_sum(a, b):
return a + b
# 2. 打印函数名.
print(get_sum) # <function get_sum at 0x000001B4AC3DD800>, 对象.
# 3. 打印函数名().
print(get_sum(10, 20)) # 调用函数, 获取返回值.
# 4. 将函数名赋值给变量, 这个变量就是: 函数对象.
my_sum = get_sum
# 5. 打印函数对象.
print(my_sum) # <function get_sum at 0x00000251CE53D800>
print(my_sum(100, 200)) # 300
案例(2): 闭包写法
案例需求
定义求和的闭包, 外部函数有参数num1, 内部函数有参数num2, 调用, 求解两数之和, 观察结果.
实现思路
- 定义外部函数.
- 定义内部函数.
- 求和.
- 调用上述函数.
- 另一种调用方式.
# 1. 定义外部函数.
def fn_outer(num1):
# 2. 定义内部函数.
def fn_inner(num2): # 有嵌套
# 3. 求和
sum = num1 + num2 # 有引用
print(f'求和结果: {sum}')
return fn_inner # 有返回
# 4. 调用上述的函数.
fn_inner = fn_outer(10)
fn_inner(20)
# 5. 另一种调用方式.
fn_inner(100)(200)
二, nonlocal关键字介绍
nonlocal关键字的定义
nonlocal关键字是Python内置的关键字, 可以实现 在内部函数中 修改外部函数的 变量值.
图解
案例需求
编写1个闭包, 让内部函数访问外部函数的参数 a = 100, 并观察结果.
实现思路
- 定义外部函数.
- 定义外部函数的(局部)变量.
- 定义内部函数, 访问外部函数的变量.
- 在内部函数中修改外部函数的变量.
- nonlocal: 可以实现在内部函数修改外部函数的变量值.
- 打印外部函数的变量.
- 返回 内部函数名(对象).
# 1. 定义外部函数.
def fn_outer()
# 2. 定义外部函数的(局部)变量.
a = 100
# 3. 定义内部函数, 访问外部函数的变量.
def fn_inner()
# 4. 在内部函数中修改外部函数的变量.
# 5. nonlocal: 可以实现在内部函数中修改外部函数的变量值.
nonlocal a
a = a + 1
# 6. 打印外部函数的变量.
print(f'a: {a}')
# 7. 返回 内部函数名(对象)
return fn_inner
# 8. 调用函数
fn_inner = fn_outer()
fn_inner() # 101
fn_inner() # 102
fn_inner() # 103
三, 装饰器及其应用场景
装饰器的定义
概述:
装饰器的本质是1个闭包函数, 目的是 在不改变原有函数的基础上, 对1其功能做增强.
大白话: 装修队 在不改变房屋结构的情况下, 对房屋做装饰(功能增强).
提前条件:
- 有嵌套.
- 有引用.
- 有返回.
- 有额外功能.
格式:
- 方法一: 传统写法.
修饰后的函数名 = 装饰器名(被装饰的原函数名)
装饰后的函数名()
- 方法二: 语法糖.
在要被装饰的原函数上, 直接写 @装饰器名, 之后直接调用原函数即可.
图解:
案例
案例(1): 发表评论.
案例需求
在发表评论前, 都是需要先登录的.
实现思路
- 定义外部函数, 形参列表接收 要被装饰的函数名(对象)
- 定义内部函数.
- 额外功能.
- 访问原函数, 即: 外部函数的引用.
- 返回内部函数对象.
- 定义函数, 表示 发表评论.
- 定义函数, 表示 充值中…
- 传统方式.
- 语法糖方式.
# 1.定义外部函数, 形参列表接收 要被装饰的函数名(对象)
def check_login(fn_name): # fn_name: 被装饰的函数名(对象)
# 2. 定义内部函数.
def fn_inner(): # 有嵌套
# 3. 额外功能
print('校验登陆... 登陆成功!')
# 4. 访问原函数, 即: 外部函数的引用.
fn_name() # 有引用
# 5. 返回内部函数对象.
return fn_inner # 有返回
# 6. 定义函数, 表示 发表评论.
def comment():
print("发表评论")
# 7. 定义函数, 表示 充值中....
@check_login # 底层其实是: payment = check_login(payment)
def payment():
print('充值中...')
# 8. 传统方式.
comment = check_login(comment)
comment()
# 9. 语法糖方式.
# payment = check_login(payment)
# payment()
payment()
案例(2): 装饰器装饰_无参无返回的原函数
细节
装饰器的内部函数格式 要和 被装饰的原函数 保持一致.
即:原函数是无参无返回的, 则 装饰器的内部函数也必须是 无参无返回的.
原函数有参有返回的, 则 装饰器的内部函数也必须是 有参有返回的.
案例需求
装饰器装饰_无参无返回的原函数
实现思路
# 需求: 定义无参无返回值的 get_sum()求和函数, 在不改变其代码的基础上, 添加友好提示: 正在努力计算中...
# 1. 定义装饰器.
def my_decorator(fn_name):
# 2. 定义内部函数, 其格式必须和 被装饰的原函数 保持一致.
def fn_inner(): # 有嵌套
# 3. 添加提示信息(额外功能)
print('正在努力计算中...') # 有额外功能
# 4. 调用原函数.
fn_name() # 有引用
# 5. 返回内部函数(对象)
return fn_inner # 有返回
# 6. 定义原函数.
@my_decorator # @装饰器名
def get_sum():
a = 10
b = 20
sum = a + b
print(f'sum求和结果: {sum}')
# 7. 传统方式.
# get_sum = my_decorator(get_sum)
# get_sum()
# 8. 语法糖方式.
get_sum()
案例(3): 装饰器装饰_有参无返回值的原函数
细节
装饰器的内部函数格式 要和 被装饰的原函数 保持一致.
即:原函数是无参无返回的, 则 装饰器的内部函数也必须是 无参无返回的.
原函数有参有返回的, 则 装饰器的内部函数也必须是 有参有返回的.
案例需求
装饰器装饰_有参无返回值的原函数
实现思路
# 需求: 定义有参无返回值的 get_sum()求和函数, 在不改变其代码的基础上, 添加友好提示: 正在努力计算中...
# 1. 定义装饰器.
def my_decorator(fn_name):
# 2. 定义内部函数
def fn_inner(x, y):
# 3. 额外功能
print('正在努力计算中...')
# 4. 调用原函数.
fn_name(x, y)
# 5. 返回内部函数.
return fn_inner
# 6. 定义原函数, 有参无返回值.
@my_decorator
def get_sum(a, b):
sum = a + b
print(f'sum求和结果: {sum}')
# 7. 传统方式.
# get_sum = my_decorator(get_sum)
# get_sum(10, 20)
# 8. 语法糖方式.
get_sum(10, 20)
四, 总结
- Q1:闭包的定义
- 概述: 内部函数 使用了 外部函数 的变量, 这种写法就称之为闭包.
- 格式: