闭包和装饰器
一、闭包
1、什么是闭包
一个完整的闭包要满足三个条件:
函数中嵌套一个函数;
外层函数返回的是内层函数的函数名;
内层函数有引用外层函数的一个非全局变量;
def func1(x,y): num = 66 def func2(): print(x) print(y) print(num * 2) return func2
每个函数都有一个__closure__,上面的闭包函数中的x,y都存在这个属性里
如:res = func1(99,"python"),打印print(res.__closure__),得到的结果是一个元组,里面三个元素,两个个int类型(66和99),一个str类型(“python”);
闭包的作用:实现内部数据的锁定,提高稳定性;
二、装饰器
1、开放封闭原则:软件实体应该是可扩展,而不可修改的,也就是说,对扩展是开放的,而对修改时封闭的;
2、装饰器的作用:在不更改原功能函数内部代码,并且不改变调用方法的情况下为原函数添加新的功能;
装饰器的应用场景:
登录验证:
函数运行时间统计;
执行函数之前做准备工作;
执行函数之后做清理工作;
1、实现一个简单的装饰器:
def login(fun): def func(): print("登录功能==输入账号、密码") print("验证通过,执行下面函数") fun() return func @login def index(): print("首页")
index()
这是一个登录的装饰器,调用首页的函数时,会先执行login函数进行登录验证,然后才执行index函数里面的代码,其中就是@login,这一行代码起的作用,
@login 相当于 index = login(index),而调用首页函数的代码,index()== login(index)();
装饰器原理:将被装饰的函数当做一个参数传入到装饰器中,并且让被装饰的函数名指向装饰器内部的函数,在装饰器的内部函数中用接收到的参数再调用被装饰的函数;
2、带参数的装饰器
def login(fun): def func(name,age): print("登录功能==输入账号、密码") print("验证通过,执行下面函数") fun(name,age) return func @login def updateinfo(name,age): print("修改信息") update("天涯","18")
3、通用装饰器
def login(fun): def func(*args,**kwargs): print("登录功能==输入账号、密码") print("验证通过,执行下面函数") fun(*args,**kwargs) return func @login def updateinfo(*args,**kwargs): print("修改信息") update("天涯","18") update()
带参数的装饰器和通用装饰器类似,只是把要传的参数放到装饰器的内层函数中,再通过传入装饰器中的函数调用即可;
4、装饰器装饰类
def login(fun): def func(*args,**kwargs): print("登录功能==输入账号、密码") print("验证通过,执行下面函数") return fun(*args,**kwargs) return func @login class Update: def __init__(self): pass UD = update() # 如果想要调用类里面的方法,就像普通类调用方法一样,如:UD = update().get_name()
装饰器装饰类的时候,装饰器内部一定要加return,因为要有一个接收对象来创建实例,如果没有加return,则UD打印出来就为None,而装饰器装饰函数时不是必须要加return
5、多个装饰器装饰
import time def login(fun): def func_login(*args,**kwargs): print("登录功能==输入账号、密码") print("验证通过,执行下面函数") fun(*args,**kwargs) return func_login def get_time(fun): def func_gettime(*args,**kwargs): print("计算运行时间的装饰器") start = time.time() fun(*args,**kwargs) end = time.time() print("运行这个函数需要:",end - start) return func_gettime @get_time @login def updateinfo(*args,**kwargs): print("修改信息") updateinfo("天涯","18") 执行结果: 计算运行时间的装饰器 登录功能==输入账号、密码 验证通过,执行下面函数 修改信息 运行这个函数需要: 0.0
通过执行结果我们可以发现,多个装饰器装饰时,装饰的过程是从下往上装饰的,执行的顺序是从上往下执行的,其中:@login相当于updateinfo = login(updateinfo),
这时的updateinfo指向的是login装饰器的func_login函数,而@get_time则相当于updateinfo = get_time(updateinfo),这时的updateinfo则指向的是func_gettime函数,
因此:
updateinfo("天涯","18") = get_time(login(updateinfo("天涯","18")))
6、类的内置装饰器
@classmethod
class MyClass: @classmethod def add(cls): print("add方法的cls:",cls) def sub(self): print("sub方法的self:",self) mc = MyClass() mc.add() mc.sub()
MyClass.add() 打印结果为: add方法的cls: <class '__main__.MyClass'> sub方法的self: <__main__.MyClass object at 0x00AC32B0>
add方法的cls: <class '__main__.MyClass'>
通过打印结果,我们可以发现,类里面的方法被@classmethod装饰器装饰之后,就变成了类方法,这时的add方法既能被实例调用,也能被类调用,而实例方法sub(),
只能被实例调用,不能被类调用;
根据编写代码规范,类方法里面用cls表示类本身,而实例方法里面用self表示实例本身;
@staticmethod
class MyClass: @staticmethod def add(self): print("add方法是一个静态方法") def sub(self): print("sub方法的self:",self) mc = MyClass() mc.add() 运行结果: add方法是一个静态方法 Traceback (most recent call last): File "C:/Users/Administrator/PycharmProjects/untitled/cekai005_lei_01.py", line 118, in <module> mc.add() TypeError: add() missing 1 required positional argument: 'self'
通过打印结果,我们可以发现,类里面的方法被@staticmethod装饰器装饰之后,就变成了静态方法,这时,不在需要传类本身代表的cls或者传实例本身代表的self参数,
而如果传入self或者cls,则会被当成位置参数,调用时需要传位置参数,不然会报错;
静态方法可被类调用,也可被实例调用;
@property
class MyClass: @property def add(self): print("add方法被property装饰器装饰后,可以像属性一样被调用") return "property" def sub(self): print("sub方法的self:",self) mc = MyClass() print(mc.add) 执行结果为: add方法被property装饰器装饰后,可以像属性一样被调用 property
通过打印结果,我们可以发现,类里面的方法被@property装饰器装饰之后,可以当做实例属性一样被实例调用;
@property装饰器的作用是对被装饰的方法设置只读属性,被装饰的方法的返回值不能被修改,修改会报错;
7、用类实现装饰器
class MyCall(object): def __init__(self,func): self.func = func def __call__(self, *args, **kwargs): print("这是类装饰器的功能") self.func() print("调用原功能函数之后的装饰器功能") @MyCall def muen(): print("实现新的功能") muen() 执行结果为: 这是类装饰器的功能 实现新的功能 调用原功能函数之后的装饰器功能
用类实现装饰器,必须搭配__init__方法使用,__init__方法还要生成一个可供传入的func函数,再定义__call__方法调用func函数来实现类装饰器;
类装饰器装饰类
用类写的装饰器不能直接用在装饰类的方法上,而是要在类方法上加一个类的内置装饰器:property,如果不加内置装饰器:property,则会报错:TypeError: area() missing 1 required positional argument: 'self';
class Mycase: def __init__(self, fun): self.fun = fun def __call__(self, *args, **kwargs): print("before") res = self.fun(*args, **kwargs) print("after") return res class Myff: def __init__(self, mm, nn): self.mm = mm self.nn = nn @property @Mycase def ff(self): print(self.mm) print(self.nn) return 111 res = Myff("m", "n").ff print(res)
除了上面加一个类的内置装饰器的方法之外,还可以再写一个将可调用对象包装成函数的装饰器来解决这个问题
class Mycase: def __init__(self, fun): self.fun = fun def __call__(self, *args, **kwargs): print("before") res = self.fun(*args, **kwargs) print("after") return res def method(call): def wrapper(*args, **kwargs): return call(*args, **kwargs) return wrapper class Myff: def __init__(self, mm, nn): self.mm = mm self.nn = nn @method @Mycase def ff(self): print(self.mm) print(self.nn) return 111 res = Myff("m", "n").ff() print(res)