zoukankan      html  css  js  c++  java
  • 装饰器

    一:装饰器简介

    1.1 什么是装饰器:

      '装饰'代指为被装饰对象添加新的功能,'器'代指器具/工具,装饰器与被装饰的对象均可以是任意可调用对象。概括地讲,装饰器的作用就是在不修改被装饰对象源代码和调用方式的前提下为被装饰对象添加额外的功能。

    1.2 为什么要用装饰器:

      软件的设计应该遵循开放封闭原则,即对扩展是开放的,而对修改是封闭的。对扩展开放,意味着有新的需求或变化时,可以对现有代码进行扩展,以适应新的情况。对修改封闭,意味着对象一旦设计完成,就可以独立完成其工作,而不要对其进行修改。

      软件包含的所有功能的源代码以及调用方式,都应该避免修改,否则一旦改错,则极有可能产生连锁反应,最终导致程序崩溃,而对于上线后的软件,新需求或者变化又层出不穷,我们必须为程序提供扩展的可能性,这就用到了装饰器。

    1.3 语法糖:

      在Python语法中,为了更简洁而优雅的使用装饰器,专门提供了一种语法来取代index=timer(index)的形式,需要在被装饰对象的正上方单独一行添加@timer,当解释器解释到@timer时就会调用timer函数,且把它正下方的函数名当做实参传入,然后将返回的结果重新赋值给原函数名

    @timer # index=timer(index)
    def index(): 
        time.sleep(3)
        print('Welcome to the index page')
        return  200

      如果在一个函数中需要使用多个装饰器,可以叠加调用,叠加多个装饰器也无特殊之处,加载顺序自上而下,执行顺序自上而下,下面会详细说明

      装饰器的实现原理有一些类似于偷梁换柱的概念,即将原函数名指向的内存地址偷梁换柱成warpper函数,因此应该将warpper做的和原函数一样才行

    二:装饰器的实现

      函数装饰器分为:无参装饰器和有参装饰两种,二者的实现原理一样,都是’函数嵌套+闭包+函数对象’的组合使用的产物。

    2.1 无参装饰器

    def outter(func):
      def wrapper(*args,**kwargs):
        # 1、调用原函数
        # 2、为其增加新功能
      res=func(*args,**kwargs)
      return res
    return wrapper

    2.2 有参装饰器

      由于语法糖@的限制,outter函数只能有一个参数,并且该参数只用来接收被装饰对象的内存地址

    def 有参装饰器(x,y,z):
        def outter(func):
            def wrapper(*args, **kwargs):
                res = func(*args, **kwargs)
                 return res
            return wrapper
        return outter
    
    @有参装饰器(1,y=2,z=3)
    def 被装饰对象():
        pass            

      可以使用help来查看函数的文档注释,本质就是查看函数的doc属性,但是对于被装饰之后的函数,查看文档注释

    print(help(home))
    打印结果:   Help on function wrapper
    in module __main__:   wrapper(*args, **kwargs)   None

    2.3 装饰器的修正

    若想要保留原函数的文档和函数名属性,需要修正装饰器
        def timer(func):
            def wrapper(*args,**kwargs):
                start_time=time.time()
                res=func(*args,**kwargs)
                stop_time=time.time()
                print('run time is %s' %(stop_time-start_time))
                return res
            wrapper.__doc__=func.__doc__
            wrapper.__name__=func.__name__
            return wrapper
    
    手动将原函数的属性赋值给warpper函数:
            1、函数wrapper.__name__ = 原函数.__name__
            2、函数wrapper.__doc__ = 原函数.__doc__
            wrapper.__name__ = func.__name__
            wrapper.__doc__ = func.__doc__
    
    Python中有一个专门的模块functools下提供了一个装饰器warps专门用来帮我们实现这件事
        from functools import wraps
    
        def timer(func):
            @wraps(func)
            def wrapper(*args,**kwargs):
                start_time=time.time()
                res=func(*args,**kwargs)
                stop_time=time.time()
                print('run time is %s' %(stop_time-start_time))
                return res
            return wrapper

    2.4 装饰器的叠加

    类装饰器:
        无参类装饰器:
            class Kuozhan():
                def __call__(self, func):           # 把对象当作函数调用的时候自动触发
                    return self.kuozhan2(func)
    
                def kuozhan1(func):
                    def newfunc():
                        print("考试前,认真复习")
                        func()
                        print("考试后,咋咋呼呼")
                    return newfunc
    
                def kuozhan2(self,func):
                    def newfunc():
                        print("考试前,开开心心")
                        func()
                        print("考试后,又哭又闹")
                    return newfunc
    
            @Kuozhan.kuozhan1
            def func():
                print("考试中......")
    
            func()
            print(">>>>>>>>>>>>>>>>>")
            @Kuozhan()
            def func():
                print("考试中......")
            func()
    
        有参类装饰器:
            class Kuozhan():
                money = "故宫门票,每人100一次,"
    
                def __init__(self,num):
                    self.num = num
    
                def __call__(self, cls):
                    if self.num == 1:
                        return self.newfunc1(cls)
                    elif self.num == 2:
                        return self.newfunc2(cls)
    
                def ad(self):
                    print("故宫一般指北京故宫。北京故宫是中国明清两代的皇家宫殿,旧称紫禁城,位于北京中轴线的中心。北京故宫以三大殿为中心....")
    
                def newfunc1(self,cls):
                    def newfunc():
                        cls.money = Kuozhan.money
                        cls.ad = Kuozhan.ad
                        return cls()
                    return newfunc
    
                def newfunc2(self,cls):
                    def newfunc():
                        if "run" in cls.__dict__:
                            res = cls.run()
                            cls.run = res
                            return cls()
                    return newfunc
    
            @Kuozhan(1)
            class MyClass():
                def run():
                    return "程序运行成功。。。"
    
    
            obj = MyClass()
            print(obj.money)
            obj.ad()
    
            @Kuozhan(2)
            class MyClass():
                def run():
                    return "程序运行成功。。。"
    
    
            obj = MyClass()
            print(obj.run)
  • 相关阅读:
    java 生成12位随机数,解决The literal 9999999999999 of type int is out of range 问题
    使用loadrunner 12 手动关联
    解析xml文件,修改Jenkins的配置
    自定义报告,用Java写一个html文件
    解决Jenkins的错误“The Server rejected the connection: None of the protocols were accepted”
    selenium项目--读取测试用例
    header("Location:login.php")
    详细设计说明书(转)
    Visual EmbedLinux Tools:让vs支持嵌入式Linux开发(转)
    phpstorm集成phpunit(转)
  • 原文地址:https://www.cnblogs.com/adcwb/p/13668152.html
Copyright © 2011-2022 走看看