• python3.x 基础三:装饰器


    装饰器:本质是函数,用于装饰其他函数,在不改变其他函数的调用和代码的前提下,增加新功能

    原则:

    1.不能修改被装饰函数的源代码

    2.不能修改被装饰函数的调用方式

    3.装饰函数对于被装饰函数透明

    参考如下链接:

    http://egon09.blog.51cto.com/9161406/1836763

    实现装饰器的需求:

    1. 函数即“变量”,函数名指向内存中的函数体,加()表示调用
    2. 高价函数,将函数当做参数传递给其他函数
    3. 嵌套函数,在函数体内再定义函数

    1.调用简单的函数:

    import time
    def test1():
        time.sleep(1)
        print('in the test1')
    test1()
    
    output: 
    in the test1

    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
    def test1():
        time.sleep(1)
        print('in the test1')
    test1()

    output:
    in the test1
    the function run time is -1.0007600784301758:
    # /usr/bin/env python
    # -*- coding: utf-8 -*-
    # Author:Jenvid.yang
    # def foo():
    #     print('in the foo')
    #     bar()
    # foo()
    # bar() 未定义,肯定会报错
    
    # def bar():
    #     print('in the bar')
    # def foo():
    #     print('in the foo')
    #     bar()
    # foo()
    
    # def foo():
    #     print('in the foo')
    #     bar()
    # def bar():
    #     print('in the bar')
    # foo()
    # 定义函数的时候,只是用函数名指向内存地址,只要函数执行步骤在函数定义之后即可,各函数间前后关系不影响
    
    # def foo():
    #     print('in the foo')
    #     bar()
    # foo()
    # def bar():
    #     print('in the bar')
    函数名及调用关系

    总结1:定义函数的时候,只是用函数名指向内存地址,只要函数执行步骤在函数定义步骤之后即可,各函数间定义先后不影响,与变量定义先后一个道理

    高阶函数

    1.把一个函数名当做实参传给另外一个函数--实现装饰器:不修改被装饰函数下,给源代码,也就是被装饰函数增加新功能

    2.返回值中包含函数名--实现装饰器:不修改函数的调用方式

    • 函数返回实参函数内存地址
    def bar():
    print('in the bar')
    def test1(func):
    print(func)
    test1(bar)
    print('分割线'.center(30,'-'))
    def test2(func):
    return func
    fff=test2(bar) # 将内存地址,也就是函数体,也就是test2返回值,赋值给一个变量
    fff() # 变量加上(),表示调用
    output:
    <function bar at 0x7f5fa0a7d730>
    -------------分割线--------------
    in the bar
    • 高阶函数传参方式实现源代码bar没有被更改,调用方式被更改了
    import time
    def bar():
        time.sleep(1)
        print('in the bar')
    def test1(func):
        start_time=time.time()
        func()
        stop_time=time.time()
        print('the function run time is %s' % (stop_time-start_time))
    test1(bar)
    output:
    in the bar
    the function run time is 1.0011107921600342
    #被调用函数原来调用方式是:bar(),函数体里面的内容也没有更改
    #新的函数是test1,调用的是test1(),改变了调用函数名,如果有生产线上已经在调用bar(),这种情况下意味着所有的调用bar函数的其他函数,都需要将bar改成test1
    • 高阶函数返回值方式实现函数调用方式不更改
    import time
    def bar():
    time.sleep(1)
    print('in the bar')
    def test1(func):
    start_time=time.time()
    return func
    stop_time=time.time()
    print('the function run time is %s' % (stop_time-start_time))
    bar=test1(bar)
    bar()
    output:
    in the bar
    # 高阶函数返回内存地址实现调用方式不更改,但函数的新功能又没加上
    • 嵌套函数,在高阶函数下,给新功能函数增加多一层函数嵌套
    import time
    def bar():
        time.sleep(1)
        print('in the bar')
    def timmer(func):
        def test1():
            start_time=time.time()
            func()
            stop_time=time.time()
            print('the function run time is %s' % (stop_time-start_time))
        return test1  #返回test1的内存地址
    bar=timmer(bar) #将bar函数地址作为实参传递给timmer函数的形参func,func将在嵌套函数test1里面进行调用
    bar() #此时的bar其实是timmer函数地址,并不是原来的bar
    output:
    in the bar
    the function run time is 1.0010881423950195
    • 语法糖
    import time
    def timmer(func):
        def test1():
            start_time=time.time()
            func()
            stop_time=time.time()
            print('the function run time is %s' % (stop_time-start_time))
        return test1
    # bar=timmer(bar)
    @timmer  #只需要在被调用函数的前面用关键字符“@装饰器函数”,既可以实现装饰器效果
    def bar():
        time.sleep(1)
        print('in the bar')
    bar()
    output:
    in the bar
    the function run time is 1.0011053085327148
    import time
    def timmer(func):
        def warpper():  #一般将装饰器嵌套函数名命名为warpper  据说懂了这个可以解决90%的问题
            start_time=time.time()
            func()
            stop_time=time.time()
            print("the function  run time is %s:" %(start_time-stop_time))
        return warpper
    @timmer  #这里还是相当于更改被装饰函数的’变量名‘内存地址 bar=timmer(bar)
    def bar():
        time.sleep(1)
        print('in the bar')
    bar()
    • 装饰器参数传值-被装饰函数传1个值
    import time
    def timmer(func):
        def warpper(name):
            start_time=time.time()
            func(name)
            stop_time=time.time()
            print("the function  run time is %s:" %(start_time-stop_time))
        return warpper
    @timmer
    def test1(name):
        time.sleep(1)
        print('in the test1 %s',name)
    test1('alex')
    output:
    in the test1 %s alex
    the function  run time is -1.0006732940673828:
    • 装饰器参数传值-被装饰函数传2个值
    import time
    def timmer(func):
        def warpper(name,age):
            start_time=time.time()
            func(name,age)
            stop_time=time.time()
            print("the function  run time is %s:" %(start_time-stop_time))
        return warpper
    @timmer
    def test1(name,age):
        time.sleep(1)
        print('in the test1 name is %s,age is %d'%(name,age))
    test1('alex',18)
    output:
    in the test1 name is alex,age is 18
    the function  run time is -1.000108003616333:
    • 装饰器参数传值-被装饰函数传n个值
    import time
    def timmer(func):
        # print('func timmer')
        def wrapper(*args,**kwargs):
            print('打印实参:',args,kwargs)
            start_time=time.time()
            func(*args,**kwargs)
            stop_time=time.time()
            print("the function  run time is %s:" % (stop_time-start_time))
        return wrapper
    @timmer
    # test1=timmer(test1)
    def test1(name,age):
        time.sleep(1)
        print('in the test1 func, name is %s,age is %d' % (name,age))
    test1('alex',18)
    
    @timmer
    # test1=timmer(test1)
    def test2(name,age,sex,salary):
        time.sleep(1)
        print('in the test1 func, name is %s,age is %d,sex is %s,salary is %d' % (name,age,sex,salary))
    test2('alex',18,'man',3000)
    output:
    打印实参: ('alex', 18) {}
    in the test1 func, name is alex,age is 18
    the function  run time is 1.0013768672943115:
    打印实参: ('alex', 18, 'man', 3000) {}
    in the test1 func, name is alex,age is 18,sex is man,salary is 3000
    the function  run time is 1.0012915134429932:
    • 试错过程:
    • import time
      def timmer(func):
          def warpper():
              # print('打印实参:',args,kwargs)
              start_time=time.time()
              func()
              stop_time=time.time()
              print("the function  run time is %s:" % (stop_time-start_time))
          return warpper
      @timmer #这里相当于test1=timmer(test1)=wrapper,test1加上参数相当于test1(name,age)=wrapper(name,age)
      def test1(name,age):
          time.sleep(1)
          print('in the test1 name is %s,age is %s'%(name,age))
      test1('alex',18)
      输出如下:
      /usr/bin/python3.5 /root/PycharmProjects/S14/day04/bin/decoreate03.py
      Traceback (most recent call last):
        File "/root/PycharmProjects/S14/day04/bin/decoreate03.py", line 17, in <module>
          test1('alex',18)
      TypeError: warpper() takes 0 positional arguments but 2 were given
      
      Process finished with exit code 1
      step1:定义装饰器函数体timmer
      step2:语法糖,调用装饰器函数,准备装饰下一行函数,返回内层嵌套函数变量warpper内存地址
      step3:定义函数体test1
      step4:调用函数test1,将test1作为实参,传递给装饰器timmer
      step5:warpper函数调用,需要讲test1的实参传递给wrapper,由于命名空间关系,test1的参数无法传递到warpper,func也无法获得实参
    • 给包装函数warrper增加形参
    • import time
      def timmer(func):
          def warpper(name,age):
              # print('打印实参:',args,kwargs)
              start_time=time.time()
              func()
              stop_time=time.time()
              print("the function  run time is %s:" % (stop_time-start_time))
          return warpper
      @timmer  #test1=timmer(test1)
      def test1(name,age):
          time.sleep(1)
          print('in the test1 name is %s,age is %s'%(name,age))
      test1('alex',18)
      输出如下
      /usr/bin/python3.5 /root/PycharmProjects/S14/day04/bin/decoreate03.py
      Traceback (most recent call last):
        File "/root/PycharmProjects/S14/day04/bin/decoreate03.py", line 17, in <module>
          test1('alex',18)
        File "/root/PycharmProjects/S14/day04/bin/decoreate03.py", line 9, in warpper
          func()
      TypeError: test1() missing 2 required positional arguments: 'name' and 'age'
      这个解释很明显,func就是test1,缺少了2个位置参数,把func()改成func(name,age)即正确
    • # /usr/bin/env python
      # -*- coding: utf-8 -*-
      # Author:Jenvid.yang
      import time
      def timmer(func): #func=test2
          def wrapper(*args,**kwargs):
              start_time=time.time()
              func(*args,**kwargs)
              stop_time=time.time()
              print('func run time is %s' %(stop_time-start_time))
          return wrapper
      
      @timmer
      def bar():
          time.sleep(1)
          print('in the bar')
      bar()
      @timmer  #test2=timmer(test2)=warpper  ->test2(name)=warpper(name)
      def test2(name):
          time.sleep(1)
          print('in the test2',name)
      test2('alex')
      课堂分析源码
    • 被装饰函数如果带有返回值的情况
    import time
    def timmer(func):
        # print('func timmer')
        def wrapper(*args,**kwargs):
            print('打印实参:',args,kwargs)
            start_time=time.time()
            func(*args,**kwargs)
            stop_time=time.time()
            print("the function  run time is %s:" % (stop_time-start_time))
        return wrapper
    @timmer
    # test1=timmer(test1)
    def test1(name,age,address):
        time.sleep(1)
        print('in the test1 func, name is %s,age is %d,address is %s' % (name,age,address))
    test1('alex',18,'nuber001')
    # @timmer
    # test1=timmer(test1)
    def test3(name,age,sex,salary):
        time.sleep(1)
        # print('in the test1 func, name is %s,age is %d,sex is %s,salary is %d' % (name,age,sex,salary))
        return (name,age,sex,salary)
    print(test3('alex',18,'man',3000))
    输出如下:
    /usr/bin/python3.5 /root/PycharmProjects/S14/day04/decoreate02.py
    打印实参: ('alex', 18, 'nuber001') {}
    in the test1 func, name is alex,age is 18,address is nuber001
    the function  run time is 1.0009291172027588:
    ('alex', 18, 'man', 3000) Process finished with exit code 0
    函数test1,没有返回值,装饰后正常输出
    函数test2,带有返回值,没有装饰,打印了实参元组
    如果test2装饰后呢?
    import time
    def timmer(func):
        # print('func timmer')
        def wrapper(*args,**kwargs):
            print('打印实参:',args,kwargs)
            start_time=time.time()
            func(*args,**kwargs)
            stop_time=time.time()
            print("the function  run time is %s:" % (stop_time-start_time))
        return wrapper
    @timmer
    # test1=timmer(test1)
    def test1(name,age,address):
        time.sleep(1)
        print('in the test1 func, name is %s,age is %d,address is %s' % (name,age,address))
    test1('alex',18,'nuber001')
    @timmer
    # test1=timmer(test1)
    def test3(name,age,sex,salary):
        time.sleep(1)
        # print('in the test1 func, name is %s,age is %d,sex is %s,salary is %d' % (name,age,sex,salary))
        return (name,age,sex,salary)
    print(test3('alex',18,'man',3000))
    输出如下:
    打印实参: ('alex', 18, 'nuber001') {}
    in the test1 func, name is alex,age is 18,address is nuber001
    the function  run time is 1.0010955333709717:
    打印实参: ('alex', 18, 'man', 3000) {}
    the function  run time is 1.0010552406311035:
    None
    带有返回值的test2被装饰后,变成了None,改变了原函数的输出,这是不允许的

    给包装函数中的调用原函数增加赋值和返回值:
    import time
    def timmer(func):
        # print('func timmer')
        def wrapper(*args,**kwargs):
            print('打印实参:',args,kwargs)
            start_time=time.time()
            res=func(*args,**kwargs)  #用一个变量保存原函数输出值
            stop_time=time.time()
            print("the function  run time is %s:" % (stop_time-start_time))
            return res #返回原函数结果给wrapper
        return wrapper
    @timmer
    # test1=timmer(test1)
    def test1(name,age,address):
        time.sleep(1)
        print('in the test1 func, name is %s,age is %d,address is %s' % (name,age,address))
    test1('alex',18,'nuber001')
    @timmer
    # test1=timmer(test1)
    def test3(name,age,sex,salary):
        time.sleep(1)
        # print('in the test1 func, name is %s,age is %d,sex is %s,salary is %d' % (name,age,sex,salary))
        return (name,age,sex,salary)
    print(test3('alex',18,'man',3000))
    输出如下:
    打印实参: ('alex', 18, 'nuber001') {}
    in the test1 func, name is alex,age is 18,address is nuber001
    the function  run time is 1.0010664463043213:
    打印实参: ('alex', 18, 'man', 3000) {}
    the function  run time is 1.0010428428649902:
    ('alex', 18, 'man', 3000)
    • 语法糖带参数情况,index使用本地验证,home使用ldap验证,同一个装饰器,如何实现不同的验证方式?
    • def deco(func):
          def wrapper(username,password):
              print('I am 包装1')
              func(username,password)
              print('I am 包装2')
          return wrapper
      @deco(auth_type='local')
      def index(username,password):
          print('welcome to index page',username)
      index('yzw','secret')
      @deco(auth_type='ldap')
      def home(username,password):
          print('welcome to home page',username)
      home('alex','secret')
    • 再嵌套一层函数
    • def auth(auth_type):
          def out_wrapper(func):
              def wrapper(username,password):
                  if auth_type == 'local':
                      print('via local certification')
                      print('I am 包装前')
                      func(username,password)
                      print('I am 包装后')
                  elif auth_type == 'ldap':
                      print('嘻嘻嘻嘻')
                      func(username, password)
                      print('via ldap certfication')
              return wrapper
          return out_wrapper
      @auth(auth_type='local')
      #1.全部不带参数,相当于index=deco(index)
      #2.被装饰函数带参数,相当于index=deco(index)=wrapper ->index(args)=wrapper(args)
      #3.语法糖带参数,就需要多嵌套一层外层函数,将语法糖的参数传递给这一外层函数
      def index(username,password):
          print('welcome to index page <local>',username)
      index('yzw','secret')
      @auth(auth_type='ldap')
      def home(username,password):
          print('welcome to home page <ldap>',username)
      home('alex','secret')
      输出如下:
      /usr/bin/python3.5 /root/PycharmProjects/S14/day04/bin/decorate04.py
      via local certification
      I am 包装前
      welcome to index page <local> yzw
      I am 包装后
      嘻嘻嘻嘻
      welcome to home page <ldap> alex
      via ldap certfication

      Process finished with exit code 0

    • #/usr/bin/env python
      #-*- coding: utf-8 -*-
      #Author:jenvid.yang
      def auth(auth_type):
          def out_wrapper(func):
              def wrapper(*args,**kwargs):
                  if auth_type == 'local':
                      print('via local certification')
                      func(*args,**kwargs)
                  elif auth_type == 'ldap':
                      print('via ldap certfication')
                      func(*args, **kwargs)
              return wrapper
          return out_wrapper
      @auth(auth_type='local')
      #1.全部不带参数,相当于index=deco(index)
      #2.被装饰函数带参数,相当于index=deco(index)=wrapper ->index(args)=wrapper(args)
      #3.语法糖带参数,就需要多嵌套一层外层函数,将语法糖的参数传递给这一外层函数
      def index(username,password):
          print('welcome to index page <local>',username)
      index('yzw','secret')
      @auth(auth_type='ldap')
      def home(username,password):
          print('welcome to home page <ldap>',username)
      home('alex','secret')
      改成万能参数
    • # /usr/bin/env python
      # -*- coding: utf-8 -*-
      # Author:Jenvid.yang
      import time
      user,passwd='alex','abc123'
      def auth(auth_type):
          print('auth func:',auth_type)
          def outer_wrapper(func):
              def wrapper(*args,**kwargs):
                  print('wrapper func args:',*args,**kwargs)
                  if auth_type == 'local':
                      username=input('username:').strip()
                      password=input('password:').strip()
                      if user == username and passwd == password:
                          print('33[32;1m user has passed authentication 33[0m')
                          res = func(*args,**kwargs)
                          print('xxxxx')
                          return res
                      else:
                          exit('33[31;1m invalid username or password 33[0m')
                  elif auth_type == 'ldap':
                      print('xxxx')
              return wrapper
          return outer_wrapper
      # @auth
      def index():
          print('welcome to index page')
      @auth(auth_type='local')  #home=wrapper()
      def home():
          print('welcom to home page')
          return ('from home')
      # 函数结果被改变
      @auth(auth_type='ldap')
      def bbs():
          print('welcome to bbs page')
      
      index()
      print(home()) # wrapper()
      bbs()
      课堂分析源码
  • 相关阅读:
    Centos7 安装python3 pip3
    VMW14.x虚拟机安装Mac10.13系统教程
    ADB命令大全
    appium服务器参数
    虚拟机VM14.X安装Mac10.12启动出现问题的解决方法
    Centos7安装vscode
    jmeter接口测试多数据组合登陆场景
    appium环境安装
    mysql命令大全
    jmeter录制请求
  • 原文地址:https://www.cnblogs.com/jenvid/p/7881681.html
Copyright © 2020-2023  润新知