一、核心要义
主要解释函数装饰器的工作原理,包括最简单的注册装饰器和较复杂的参数化装饰器。同时,因为装饰器的实现依赖于闭包,因此会首先介绍闭包存在的原因和工作原理。
二、代码示例
1、变量作用域规则
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2024/2/3 11:26
# @Author : Maple
# @File : 01-变量作用域规则.py
# @Software: PyCharm
b = 10
def f1(a):
print(a)
print(b)
# b = 20 # 在函数体中给b赋值,因此会被判断为局部变量
def f1_revise(a):
global b # 声明函数体中的b为全局变量
print(a)
print(b)
b = 20
"""列表的作用域"""
students = []
def f2():
# students指向全局变量
students.append('a')
return id(students)
def f3():
# 内部再声明一个 students,其为局部变量,与外部的students不是同一个对象
students = []
students.append('a')
return id(students)
def f4():
# 声明全局变量
global students
students +=[1]
# print(id(students))
return id(students)
if __name__ == '__main__':
# 1. f1测试
# 说明: 1.Python在编译函数f1的定义体时,判断b为局部变量,因为在函数体中给b赋值了
# 2.所以在调用函数f1(10)的时候,执行到print(b),发现局部变量b还没有赋值,此时就会报错
#f1(10) # UnboundLocalError: local variable 'b' referenced before assignment
# 2. f1_revise测试
f1_revise(20) # 20,10
# 全局变量b的值被修改
print(b) # 20
# 3.f2测试
print(f2() == (id(students))) #True
# 4.f3测试
print(f3() == (id(students))) #False
# 5.f4测试
print(f4() == (id(students))) #True
2、闭包
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2024/2/3 11:35
# @Author : Maple
# @File : 02-闭包.py
# @Software: PyCharm
"""需求:计算移动平局值
每调用一次函数,传入一个新的数值,然后和前面的所有值进行累加,再计算最后的平均值
"""
# 1. 借用数组方式实现
def make_avg():
num_list = []
def avg(new_value):
num_list.append(new_value)
total = sum(num_list)
return total / len(num_list)
return avg
# 2.直接使用变量方式实现:但存在一个坑
def make_avg_revise1():
count = 0
total = 0
def avg(new_value):
count += 1
total += new_value
return total / count
return avg
# 3.直接使用变量方式实现:填坑
def make_avg_revise2():
count = 0
total = 0
def avg(new_value):
nonlocal count,total # 通过nonlocal 将变量标记为`自由变量`
count += 1
total += new_value
return total / count
return avg
if __name__ == '__main__':
print("***1. make_avg 测试**********************")
# 1. make_avg 测试
avg = make_avg()
# 分析:1.按理说调用完 make_avg_revise1()返回avg1后,make_avg_revise1函数中的局部变量num_list的作用域应该消失了
# 2.但实际上,在avg1中仍然能够调用num_list,这就是所谓闭包现象(变量的作用域外延了)
# 3.num_list被称作`自由变量`
print(avg(5)) # 5.0
print(avg(10)) # 7.5
# 查看avg1 创建和绑定的变量
## 1-1 创建的局部变量
print(avg.__code__.co_varnames) # ('new_value', 'total')
## 1-2 绑定的自由变量
print(avg.__code__.co_freevars) # ('num_list',)
## 自由变量num_list绑定在avg1的closure属性中:是一个cell对象
print(avg.__closure__) # (<cell at 0x000001C6095CA760: list object at 0x000001C6095C0900>,)
## num_list的值则在cell对象中的cell_contents属性中
print(avg.__closure__[0].cell_contents) # [5, 10]
print("*** 2.调用make_avg_revise1会报错**********************")
# 2.调用make_avg_revise1会报错
# 分析:1. 内层函数avg的变量count 和 total在函数内部赋值,因为在函数体编译的时候,会被当作局部变量,但是又没有初始化声明,所以
# 当函数函数调用的时候,会报错
try:
avg = make_avg_revise1()
print(avg(5))
except Exception as e:
print(e) #local variable 'count' referenced before assignment
print("*** 3.make_avg_revise2测试**********************")
# 3.make_avg_re