在Python编程中,闭包和装饰器是两个极其强大的特性,它们提供了代码复用和增强的高级手段。本文将深入探讨这两个概念,通过实例展示它们的应用和特点。
一、闭包:记忆的力量
闭包(Closure)是指在一个内部函数中,对外部作用域的变量进行引用。简单来说,闭包使得函数可以记住并访问其外部函数的变量,即使外部函数已经执行完毕。
闭包的特点:
- 外部函数嵌套内部函数
- 外部函数将内部函数返回
- 内部函数可以访问外部函数的局部变量
a = 20
def my_fun1():
print(f'my_fun1')
i = 10
def my_fun2():
nonlocal i # 声明 i 是局部变量
global a # 声明 a 是全局变量
a += 30
i += 20
print(f'my_fun2', a, i)
my_fun2()
return my_fun2
r = my_fun1
r() #第一次执行
# my_fun1
# my_fun2 50 30
r() #第二次执行
# my_fun1
# my_fun2 80 30
r() #第三次执行
# my_fun1
# my_fun2 110 30
请注意,每次直接调用 r()
实际上是调用 my_fun2
,而 i
的值会在每次 my_fun1
调用时重新初始化,因为 i
是 my_fun1
的局部变量。而 a
是全局变量,所以它的值会在每次调用 my_fun2
时累积增加。
二、装饰器:函数的魔法外衣
装饰器(Decorator)是一种设计模式,用于在不修改原始函数代码的情况下,增加函数的功能。装饰器本身是一个函数,它接受一个函数作为参数,并返回一个新的函数。
装饰器的特点:
- 增强函数功能:在不改变原有逻辑的情况下,增加新的功能。
- 可重用性:装饰器可以应用于任何函数,实现功能的重用。
- AOP(面向切面编程):用于日志记录、性能测试、事务处理等跨关注点问题。
# 装饰器:一个函数,用于增强或修改另一个函数的行为,通常通过返回一个新的函数来实现。
import random
import time
datas = [random.randint(0, 10000) for i in range(10000)]
# 通过浅拷贝 得到一模一样的列表
datas_copy = datas.copy()
def time_cost(f):
def calc():
stat = time.time()
f()
print(f'{f}花费的时间开销为:{time.time() - stat}')
return calc
@time_cost
def my_fun1():
datas.sort()
print(datas)
my_fun1()
@time_cost
def my_fun2():
new_list = sorted(datas_copy)
print(new_list)
my_fun2()
这里使用 @time_cost
语法,我们为 my_fun
添加了打印随机数花费的时间开销。
为了能让大家更好的理解装饰器换一种写法来演示下:
# 装饰器:一个函数,用于增强或修改另一个函数的行为,通常通过返回一个新的函数来实现。
import random
import time
datas = [random.randint(0, 10000) for i in range(10000)]
# 通过浅拷贝 得到一模一样的列表
datas_copy = datas.copy()
def time_cost(f):
def calc():
stat = time.time()
f()
print(f'{f.__name__}花费的时间开销为:{time.time() - stat}')
return calc
def fun1():
datas.sort()
print(datas)
fun1 = time_cost(fun1)
fun1() # 此时的fun1()不是fun1()而是calc()
def fun2():
new_list = sorted(datas_copy)
print(new_list)
fun2 = time_cost(fun2)
fun2() # 此时的fun2()不是fun2()而是calc()
三、 闭包与装饰器的结合
那怎么解决 被修饰的函数有参数的情况呢?我们还拿上边的例子举例:
# 解决 被装饰的函数有参数
import random
import copy
import time
datas = [random.randint(0, 10000) for i in range(10000)]
datas_copy = copy.deepcopy(datas)
def time_cost(f):
def calc(sort_type):
start = time.time()
f(sort_type)
print(f'函数{f.__name__}消耗的时间为:{time.time() - start}')
return calc
def my_fun1(sort_type):
datas.sort(reverse=sort_type)
print(datas)
r = time_cost(my_fun1)
r(True)
def my_fun2(sort_type):
new_datas = sorted(datas_copy, reverse=sort_type)
print(new_datas)
r = time_cost(my_fun2)
r(True)
- 当调用
r(True)
时,实际上是调用了装饰后的my_fun1
函数,并传递True
作为参数。这会测量my_fun1
函数执行所需的时间,并打印出排序后的datas
列表。 - 接着,再次调用
r(True)
时,实际上是调用了装饰后的my_fun2
函数,并传递True
作为参数。这会测量my_fun2
函数执行所需的时间,并打印出新的排序列表new_datas
。
这里再附带一个实际的代码,当我们进入某网站首页时,登陆了才能看购物车和个人中心。
user = None
def login_req(f):
def check():
global user
if user:
f()
else:
while True:
username = input('请输入用户名:')
password = input('请输入密码:')
if username == 'admin' and password == '123456':
user = username
f()
break
else:
print('用户名或密码错误')
return check
def index():
print(f'我是首页')
@login_req
def center():
print(f'我是个人中心')
@login_req
def cart():
print(f'我是购物车')
index()
center()
cart()
结论
闭包和装饰器是Python中两个强大的工具,它们提供了代码复用和增强的新途径。闭包以其记忆作用域的能力,允许函数访问外部作用域的变量;而装饰器以其增强功能和可重用性,允许我们在不修改原始函数代码的前提下,增加新的功能。理解并掌握这两个概念,将极大地提升你的Python编程能力。