闭包和装饰器

一、闭包

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)
原文地址:https://www.cnblogs.com/lzh501/p/10878934.html