闭包&装饰器
闭包条件
- 作用域 L_E_G_B
- 函数中嵌套函数
- 外层函数返回内存嵌套函数名
- 嵌套函数有引用一个非全局据变量(E作用域)
def func():
a = 100 # Enclosed作用于
def inner():
print(a)
return inner
f = func()
print(f) # 返回inner函数对象
f() # 执行inner函数
作用:实现数据锁定,提高稳定性
装饰器
-
知识储备:
- 作用域 L_E_G_B
- 高阶函数 :1.函数名可以作为参数传入;2.函数名还可以作为返回值
- 闭包
-
解释:
- 装饰函数是一个闭包样式的函数,参数是被装饰函数
- 被装饰函数会当作参数传入装饰函数中,然后返回给被装饰函数接收,所以我们执行被装饰函数的时候就等于执行装饰器函数的返回值--嵌套函数
-
目的:遵循原则,开放扩展 关闭修改
-
好处
- 在不改变原函数功能的情况扩展功能
- 执行原函数就能执行到扩展功能
-
装饰器的应用场景
- 登陆验证
- 函数运行时间统计
- 执行函数之前做准备工作
- 执行函数后做清理功能
# 添加时间统计的装饰器
def add_time(func):
# *args, **kwargs作用:有参数无参数的函数都能装饰
def inner(*args, **kwargs):
before_time = time.time()
func(*args, **kwargs)
after_time = time.time()
print(after_time - before_time)
return inner
@add_time
def for_fun(): # for_fun=add_time(for_fun) add_time(for_fun) == inner
for i in range(100):
if i == 50:
time.sleep(1)
print(i)
# 所以执行for_fun的时候等于执行的inner
for_fun()
# 登陆验证的装饰器,每个页面验证登陆 如果登陆过不需要重复登陆
# txt数据
# {"name":"a","passwd":"1"}
# {"name":"b","passwd":"2"}
# {"name":"c","passwd":"3"}
cookie = "False"
def check_login(func):
def inner(*args, **kwargs):
global cookie
if cookie == "False":
print("身份验证失败请重新登陆")
n = 0
while n < 4:
username = input("请输入你的用户名").strip()
passwd = input("请输入你的密码").strip()
# 读取用户信息文件,判断帐号密码
with open("login_info.txt", "r", encoding="utf-8") as file:
for line_info in file:
user_info = eval(line_info.strip())
if username == user_info["name"] and passwd == user_info["passwd"]:
print("登陆成功")
cookie = "True"
func(*args, **kwargs)
else:
print("用户名不存在或密码错误")
n += 1
else:
print("错误次数过多,退出登陆")
else:
func(*args, **kwargs)
return inner
@check_login
def home():
print("欢迎来到首页")
@check_login
def me():
print("欢迎来到个人中心")
print(home())
print(me())
- 装饰器装饰类
- 将类对象传进装饰器,逻辑和装饰函数一样
- 嵌套函数需要返回类实例对象
# 类装饰器,reture 实例对象即可
def Myfunc(cls):
def inner(*args, **kwargs):
print("进入装饰器")
# cls是传进来的类 被执行实例化对象,初始化实例对象初始值
# # 必须将对象返回给调用方接收实例
return cls(*args, **kwargs)
return inner
@Myfunc
class MyClass: # MyClass=Myfunc(MyClass)
def __init__(self, name, age):
self.name = name
self.age = age
# 实例化对象是执行装饰器内层函数后 返回的一个实例对象给m接收
m = MyClass("DaBai", 18)
print(m) # <__main__.MyClass object at 0x000001E76D6A0358> 实例对象
print(m.name)
类中常用的三个装饰器
- @classmethod 标记为类方法,默认参数为"cls",类本身可以直接调用(类属性,类方法,类本身和实例都可以直接调用,实例属性和实例方法,类本身不能调用,所以将方法加上@classmethod 装饰器变成类方法后类本身就可以直接调用了)
- @staticmethod 标记为静态方法,默认不需要传参,类和实例都能调用
- @property 设置只读属性,被property装饰的方法可以像属性一样被实例调用,但是属性值为只读属性,不能被修改(毕竟原始还是个方法)
# 三个类装饰器
class MyTest(object):
def __init__(self, name):
self.name = name
@classmethod # 类方法
def add(cls): # 默认参数传cls==类本身,self==实例本身
print("add方法")
# print(cls)
@staticmethod # 静态方法 类本身和实例对象都能调用
def static(): # 默认不需要传参数,不像类方法默认传cls,实例方法默认传self
print("这个是静态方法")
@property # 作用:设置实例的只读属性,不允许被修改 ,不允许修改的属性这样设置
def Name(self):
print("被这个装饰的类方法,可以像是属性一样被调用")
name = "pro"
return name
def run(self):
print(self.name) # 实例属性 name 可以修改
print(self.Name) # 被@property装饰的方法可以像属性一样调用,不可被修改
# 实例
t = MyTest("DaBai")
# 类方法(classmethod)
MyTest.add() # 类直接调用
t.add() # 实例也能调用
# 静态方法(staticmethod)
MyTest.static() # 类直接调用
t.static() # 实例也能调用
# 只读方法(property)实例方法可以像属性一样调用
# 实例属性允许被修改或者添加
print(t.name)
t.name = "Bai"
print(t.name)
# property 设置只读属性不允许被修修
# t.Name = "PRO" # 报错:AttributeError: can't set attribute
print(t.Name) # 被property装饰的方法可以像属性一样调用 但是属性值为只读属性,不能被修改(毕竟原始还是个方法)
t.run()