• 闭包和装饰器


    一、闭包

    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)
  • 相关阅读:
    linux shell创建目录、遍历子目录
    linux shell写入单行、多行内容到文件
    如何起个好名字
    linux shell编程中的数组定义、遍历
    详解浏览器分段请求基础——Range,助你了解断点续传基础
    实现一个大文件上传和断点续传
    localStorage设置过期时间
    Python3 __slots__
    Nginx 流量统计分析
    argparse简要用法总结
  • 原文地址:https://www.cnblogs.com/lzh501/p/10878934.html
Copyright © 2020-2023  润新知