一、闭包
先补充一下变量的生命周期这个知识点,对于局部变量,它会随着函数的调用生成,函数调用完成就会释放,例如:
def func1():
print("i am func1")
def outer():
#设置x局部变量
x = 30
func1()
print(f"x is {x}")
outer()
#变量x会随着函数调用的结束而释放
print(x)
由于函数调用的结束,局部变量x被释放,所以打印x会报NameError的错误。
闭包形成的三个条件:
- 必须要有内外函数
- 外函数必须返回内函数
- 内函数必须引用外函数的变量
例如:
def outer(x):
a = 200
def inner():
print(x + a)
#返回函数本身,不是返回内函数的调用结果,函数名后面不加()
return inner
#调用外函数outer,调用结束后,没有释放a和x
d = outer(100)
#调用内函数,输出x+a的值
d()
二、装饰器
装饰器是一种程序设计模式,主要用于在不改变函数或类源代码的基础上添加一些额外功能。装饰器本质就是闭包函数,它需要把一个callable对象(函数、类)作为参数传入。装饰器只能装饰callable对象(函数、类)。
例:写一个计算函数运行时间的装饰器
import time
def runtime(func):
def inner(*args, **kwargs):
#获取当前时间戳
start = time.time()
#*args, **kwargs让装饰器更加通用
result = func(*args, **kwargs) #执行原函数
#获取原函数运行结束后的时间戳
end = time.time()
print(f"函数执行花费了{end - start}s")
#inner函数返回原函数的返回值
return result
return inner
使用装饰器,用@修饰符:
@runtime #相当于func = runtime(func)
def func(a, b):
#睡眠2秒
time.sleep(2)
return a + b
#被装饰过的函数就不是原函数了,是装饰器返回的内函数
func(1, 3)
print(f"func函数运行结果为{result}")
运行结果:
例:编写一个记录日志的装饰器,记录INFO等级并写到文件和屏幕
import logging
from logging import FileHandler, StreamHandler
def log(func):
def inner_log(*args, **kwargs):
logger = logging.getLogger()
file = FileHandler("sc.log")
stream = StreamHandler()
#指定格式记录日志
log_format = "%(asctime)s | %(filename)s | %(levelname)s:%(message)s"
formatter = logging.Formatter(log_format)
file.setFormatter(formatter)
stream.setFormatter(formatter)
logger.addHandler(file)
logger.addHandler(stream)
logger.setLevel(logging.INFO)
logger.info(f"开始执行{func.__name__}函数!")
result = func(*args, **kwargs)
logger.info(f"{func.__name__}函数执行结束!")
return result
return inner_log
使用上文编写的两个装饰器--log和runtime装饰器,装饰一个函数:
#被两个装饰器装饰后,MyFunc为log的内函数
@log #log装饰的是runtime的内函数
@runtime #runtime装饰的是原来的MyFunc函数
def Myfunc():
time.sleep(1)
print("this is MyFunc")
#此时的Myfunc = log(runtime(Myfunc))
Myfunc()
#注意装饰器的先后顺序
运行结果:
三、带参数的装饰器
自身不传入参数的装饰器,使用两层函数定义;自身传入参数的装饰器,使用三层函数定义。
例:使用装饰器带参数来验证用户是否有权限来运行函数。add函数添加权限认证装饰器,判断一:判断用户名和密码是否正确,不正确提示用户名和密码错误,不能执行add函数;判断二、判断用户是否为root,只有root有权限执行函数。其中用户名和密码通过带参数的装饰器传入。
user = {"root": "123456", "admin": "admin123"}
name = input("请输入用户名:")
password = input("请输入密码:")
def check(userdict, username, password):
def checkfunc(func):
def inner(*args, **kwargs):
if userdict.get(username) == password:
if username == "root":
func(*args, **kwargs)
print(f"{func.__name__}函数运行成功!")
else:
print(f"{username}用户没有权限")
else:
print("用户名或密码错误!")
return inner
return checkfunc
@check(userdict=user, username=name, password=password)
def add(a, b):
print(f"{a} + {b} = {a + b}")
add(1, 2)
add函数执行成功运行结果:
add函数未能成功执行的运行结果:
四、用类实现装饰器
例:用类实现计时装饰器
import time
class runtime():
def __init__(self, func):
self.func = func
#把实例化后的对象当做函数调用的时候自动执行
def __call__(self, *args, **kwargs):
#在内函数中实现的功能,在类中放在__call__中
start = time.time()
result = self.func(*args, **kwargs)
end = time.time()
print(f"执行函数花费了{end - start}s")
return result
@runtime #func1 = runtime(func1)
def func1():
time.sleep(1)
print("i am func1")
func1()
运行结果:
用类实现带参数的装饰器:改写上文写过的权限认证装饰器
class check:
def __init__(self, userdict, username, password):
self.userdict = userdict
self.username = username
self.password = password
def __call__(self, func):
def inner(*args, **kwargs):
if self.userdict.get(self.username) == self.password:
if self.username == "root":
func(*args, **kwargs)
print(f"{func.__name}函数执行成功!")
else:
print(f"{self.username}用户权限不够!")
else:
print("用户名或密码错误!")
return inner
user = {"root": "123456", "admin": "admin123"}
name = input("请输入用户名:")
password = input("请输入密码:")
@check(userdict=user, username=name, password=password)
def add(a, b):
print(f"{a} + {b} = {a + b}")
#a = check(userdict=user, username=name, password=password), add = a(add)
add(1, 2)
五、装饰类
例:
def outer(cls):
def inner(*args, **kwargs):
print(f"class name is {cls.__name__}")
return cls(*args, **kwargs)
return inner
@outer #A = outer(A)
class A:
def __init__(self, name):
self.name = name
#a1为装饰器outer中内函数返回的一个对象
a1 = A("sc")
print(f"name of a1 is {a1.name}")
运行结果: