• Python中的装饰器


    装饰器

    • 装饰器定义;
      • 本质是函数 函数的目的是完成特定的功能
    • 装饰器功能:一个装饰其他函数功能的函数(为其他函数添加特定的功能)

    抛出问题:

      假如我们现在有10个函数,每个函数都有自己独特的功能,但是,现在我们需要给这10个函数添加一个记录日志的功能

    # 定义日志的函数,然后将日志函数添加到test的十个函数中
    def logger():
        print("...logger...")
    
    def test1():
        pass
        logger()
    
    def test2():
        pass
        logger()
    
    def test3():
        pass
        logger()
    
    test1()
    test2()
    test3()
    使用添加函数的方法

      特定场景:假如,这10个函数已经再线上运行了,比如说,现在需要再用户已经使用的软件中,给这10个函数添加新的功能,那么该怎么做?

      • 如果我们直接修改函数的源代码,可能会导致软件崩溃,所以原则上,我们不能在已经上线的程序中修改源代码。

    装饰器原则:

    • 原则1:不能修改被装饰的函数的源代码
    • 原则2:不能修改被装饰的函数的调用方式
    • 装饰器对被装饰的函数是完全透明的,函数感知不到装饰器的存在,因为函数的源代码和调用方式都没有改变

    初识装饰器:

    • 计算函数运行时间的装饰器
      import time
      # 创建获取函数运行时间的装饰器
      def timmer(func):
          def warpper(*args, **kwargs):
              start_time = time.time()
              func()
              stop_time = time.time()
              print("the func run time is %s" % (stop_time-start_time))
          return warpper
      
      @timmer     #为test函数添加获取test函数运行时间的功能
      def test():
          time.sleep(3)
          print("in the test")
      
      test()
      """
      运行效果:
      in the test
      the func run time is 3.004085063934326
      """
    • 由上代码可以得知:
      • 装饰器本身就是一个函数
      • 装饰器不修改被装饰函数的源代码、同时也不修改被装饰函数的调用方式
      • 对于test函数来说,装饰器timmer就和不存在一样

    实现装饰器的知识储备:

    • 函数即”变量“
    • 高阶函数
    • 嵌套函数

      高阶函数 + 嵌套函数 =》 装饰器

    函数即“变量”:

    # 第一种
    def foo():
        print("in the foo")
        bar()
    foo()
    
    第二种
    def foo():
        print("in the foo")
        bar()
    def bar():
        print("in the bar")
    foo()
    
    # 第三种
    def bar():
        print("in the bar")
    def foo():
        print("in the foo")
        bar()
    foo()
    
    # 第四种
    def foo():
        print("in the foo")
        bar()
    foo()
    def bar():
        print("in the bar")
    分析内存地址

    高阶函数:

    • 把一个函数名当作实参传递给另一个函数:

      import time
      def bar():
          time.sleep(3)
          print("in the bar")
      # print(bar) # bar记录了bar函数在内存中的地址
      
      def foo(func):
          start_time = time.time()
          func()
          stop_time = time.time()
          print("the func run time is %s" % (stop_time - start_time))
      
      # 满足了装饰器的不修改被装饰器的源代码的条件,但是调用方式被修改了
      foo(bar) 
      """
      运行结果:
      in the bar
      the func run time is 3.0134708881378174
      """
      符合装饰器的条件之一
      • 在不修改被装饰函数源代码的情况下为其添加功能
    • 返回值中包含函数名
      import time
      def bar():
          time.sleep(3)
          print("in the bar")
      
      def foo(func):
          start_time = time.time()
          func()
          stop_time = time.time()
          print("the func run time is %s" % (stop_time - start_time))
          return func
      
      # foo(bar())
      
      #print(foo(bar))
      
      # t = foo(bar)
      # print(t)
      
      # t = foo(bar)
      # t()
      # 不修改函数的调用方式,为其添加功能
      bar = foo(bar)
      bar()
      符合装饰器的条件之二
      • 不修改被装饰函数的调用方式

    嵌套函数:

    • 在函数体内使用def关键字定义一个新的函数
      # 嵌套函数
      def foo():
          print("in the foo")
          def bar():
              print("in the bar")
          bar()
      # bar() #报错,因为bar的作用域仅在foo()函数体内,当foo函数运行结束,那么bar就会释放空间
      foo()
      嵌套函数
    • 变量作用域的访问顺序
      x = 0
      def grandfather():
          x = 1
          def father():
              x = 2
              def son():
                  x = 3
                  print(x)
              son()
          father()
      grandfather()
      代码演示

    闭包:

      当局部变量脱离了函数体后,依然可以使用:

    def foo():
        sum_1 = 100
        def deco():
            print(sum_1)
        return deco
    a = foo()
    a()     #100
    # print(sum_1) # 报错,局部变量不能在局部以外的地方使用
    演示闭包

      解释代码:

    def foo(x):
        def deco(y):
            print(x+y)
        return deco
    
    a = foo(10)
    print(a)    #<function foo.<locals>.deco at 0x00000275D6421040>
    a(5)        # 15
    b = foo(20)
    print(b)    #<function foo.<locals>.deco at 0x0000017D992D10D0>
    b(5)        #25
    
    """
    所有函数都有一个__closure__属性,如果这个函数是一个闭包的话,那么它返回的是一个由cell对象组成
    的元组对象。cell对象的cell_contents属性就是闭包中的自由变量
    """
    print(foo.__closure__)  #None
    print(a.__closure__)    #(<cell at 0x000002B1F02C1370: int object at 0x000002B1F0116A50>,)
    print(b.__closure__)    #(<cell at 0x000002B1F018A0D0: int object at 0x000002B1F0116B90>,)
    print(a.__closure__[0].cell_contents)   #10
    print(b.__closure__[0].cell_contents)   #20
    """
    这解释了为什么局部变量脱离函数之后,还可以在函数之外被访问的原因,因为它存储在了闭包的cell_contens中了
    """
    演示闭包原理

    实现装饰器:

    import time
    def timer(func):
        def deco():
            start_time = time.time()
            func()
            stop_time = time.time()
            print("the func run time is %s" % (stop_time - start_time))
        return deco
    
    @timer
    def test1():
        time.sleep(3)
        print("in the test1")
    @timer
    def test2():
        time.sleep(3)
        print("in the test2")
    
    # test1 = timer(test1)
    # test2 = timer(test2)
    test1()
    test2()

    改进后的装饰器:

    import time
    def timer(func):
        def deco(*args, **kwargs):
            start_time = time.time()
            func(*args, **kwargs)
            stop_time = time.time()
            print("the func run time is %s" % (stop_time - start_time))
        return deco
    
    @timer
    def test1():
        time.sleep(3)
        print("in the test1")
    @timer
    def test2(name):
        time.sleep(3)
        print("in the test2 %s"% name)
    
    test1()
    test2("python")

     优化后的装饰器:

    import time
    def timer(func):
        def deco(*args, **kwargs):
            start_time = time.time()
            res = func(*args, **kwargs)
            stop_time = time.time()
            print("the func run time is %s" % (stop_time - start_time))
            return res
        return deco
    
    @timer
    def test1():
        time.sleep(1)
        print("in the test1")
    @timer
    def test2(name):
        time.sleep(2)
        print("in the test2 %s"% name)
    
    @timer
    def test3(name, age):
        time.sleep(3)
        print("in the test3 %s"% name)
        return age+1
    
    test1()
    test2("python")
    #test3("某人飞",999)
    print(test3("某人飞",999))

    加强版装饰器:

    # 加强版装饰器
    usern = "fjf"
    passwd = "123456"
    def dl(func,*args, **kwargs):
        username = input("请输入您的账号")
        password = input("请输入您的密码")
        if username == usern and password == passwd:
            print("登录成功")
            res = func(*args, **kwargs)
            return res
        else:
            print("您的账号或者密码错误")
    
    def login(auth_type):
        def wrapper_inout(func):
            def wrapper(*args, **kwargs):
                if auth_type == "loca":
                    print("通过loca验证")
                    dl(func, *args, **kwargs)
    
                elif auth_type == "loca1":
                    print("通过loca1验证")
                    dl(func, *args, **kwargs)
    
                elif auth_type == "ip":
                    print("通过ip验证")
                    dl(func, *args, **kwargs)
                else:
                    print("暂不支持其他登录方式")
            return wrapper
        return wrapper_inout
    
    @login(auth_type="loca")
    def qq_user():
        print("欢迎使用qq")
    
    @login(auth_type="loca1")
    def wx_user(name):
        print("欢迎%s使用微信"%name)
    
    @login(auth_type="ip")
    def wb_user(name, age):
        print("欢迎%s使用微博"%name)
        return age >= 18
    
    @login(auth_type="smdx")
    def ys():
        pass
    
    ys()
    qq_user()
    wx_user("某人飞")
    print(wb_user("某人飞", 17))
    """
    闭包中的拓展知识点:
    导入functools模块:
    1.@functools.wraps(func):在闭包中将原函数func的文件名、注释文档等,传递给inner函数。
    2.func.__name__:获取函数的名字(字符串), 比如:func 这是一个字符串,并不是一个func函数的地址
    3.func.__doc__:获取函数的注释文档
    """
    import functools
    
    
    def auth(func):
        @functools.wraps(func)  # inner.__name=func.__name   inner.__doc__=func.__doc__
        def inner(*args, **kwargs): 
            """这个是inner函数的注释文档"""
            # 在原函数func执行之前添加的功能
            res = func(*args, **kwargs)  # 对接收到的参数进行拆包,传递给原函数func()
            # 在原函数func执行之之后添加的功能
            return res  # 将原函数func的返回值返回给调用者
        return inner
    
    
    @auth
    def func():
        """这个是func函数的注释文档"""
        print("this is func")
        return "func"
    
    
    print(func.__name__)    # 原结果:inner  加上代码 @functools.wraps(func) 后结果为:func
    print(func.__doc__)     # 原结果:这个是inner函数的注释文档   加上代码 @functools.wraps(func) 后结果为:这个是func函数的注释文档

     

  • 相关阅读:
    十、collection的作用+变量
    python借助ADB工具实现自动化操作手机
    selenium 对浏览器标签页进行关闭和切换
    python编写shell脚本
    报错解决——# Creating Server TCP listening socket *:6379: bind: Address already in use
    Mac 报错:-bash: telnet: command not found
    Mac快捷键大全及cheatsheet插件
    报错解决——xcrun: error: invalid active developer path (/Library/Developer/CommandLineTools), missing xcrun at: /Library/Developer/CommandLineTools/usr/bin/xcrun
    Mac破解免费office软件
    python 当前时间多加一天、一小时、一分钟
  • 原文地址:https://www.cnblogs.com/fjfsu/p/15651695.html
Copyright © 2020-2023  润新知