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

    前戏

    装饰器:
    定义:本质是函数,(装饰其他函数)就是为其他函数添加附加功能

    原则:

    • 不能修改被装饰的函数的源代码
    • 不能修改被装饰的函数的调用方式

    实现装饰器知识储备

      1.函数即'变量'

      2.高阶函数

      • 把一个函数当做实参传递给另外一个参数(在不修改被装饰函数源代码的情况下为其添加功能)
      • 返回值中包含函数名(不修改函数的调用方式)

      3.嵌套函数(在一个函数的函数体内用def声明另一个函数)

    无参数的装饰器

    下面有一个函数,如果我们想统计一下函数执行的时间,使用装饰器怎么实现呢

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

    如果不使用装饰器我们可以在之前前加一个时间,执行后加一个时间就可以了,如下

    import time
    
    
    def test1():
        start_time = time.time()
        time.sleep(2)
        print('in the test1')
        print(time.time()-start_time)
    
    
    test1()

    但是这样的话,我们修改了源代码,如果这个函数有好多地方调用了,有些地方不希望要其他的功能,有些地方需要,难道我们需要重新写一个函数吗?在把调用到的地方都修改一下?如果你这样的话,明天就可以去财务领工资了。。。那来看看怎么使用装饰器来实现

     1 import time
     2 
     3 
     4 def timmer(func):
     5     def warpper():
     6         start_time = time.time()
     7         func()  # run test1
     8         stop_time = time.time()
     9         print('the func time is %s' % (stop_time - start_time))
    10 
    11     return warpper
    12 
    13 
    14 @timmer  # test1=timmer(test1)语法糖,给哪个函数加装饰器,就在哪个函数前加上 @ 装饰名
    15 def test1():
    16     time.sleep(2)
    17     print('in the test1')
    18 
    19 test1()  # 运行warpper()

    代码解释:

    函数从上往下执行,执行到14行的时候,14行的代码等价于test1=timmer(test1)。就回去执行timmer函数,所以timmer里的func就是test1。执行完返回了warpper,所以test1=warpper,然后执行19行,因为test1=warpper,所以19行相当于warpper(),执行了waepper函数,而func为test1,func()就是调用了test1()这个函数。

    这样我们就实现了一个装饰器,在不修改源代码和调用方式的情况下给函数增加了新的功能。

    带参数的装饰器

    上面的test1函数没有参数,如果test1需要一个参数,还这样写的话就会报错

    import time
    
    
    def timmer(func):
        def warpper():
            start_time = time.time()
            func()  # run test1
            stop_time = time.time()
            print('the func time is %s' % (stop_time - start_time))
    
        return warpper
    
    
    @timmer  
    def test1(m1):
        time.sleep(2)
        print('in the test1')
    
    test1('2')  

    结果:

    TypeError: warpper() takes 0 positional arguments but 1 was given

    报错信息告诉了我们warpper需要一个参数,那我们去修改一下

    import time
    
    
    def timmer(func):
        def warpper(arg):
            start_time = time.time()
            func(arg)  # run test1
            stop_time = time.time()
            print('the func time is %s' % (stop_time - start_time))
    
        return warpper
    
    
    @timmer
    def test1(m1):
        time.sleep(2)
        print('in the test1')
    
    test1('2')

    我们给warpper传了一个参数arg,因为func(test1)是需要一个参数的,那如果test1需要多个参数怎么办呢,我们可以用可变参数*args,**kwargs来实现动态的传参

    import time
    
    
    def timmer(func):
        def warpper(*args, **kwargs):
            start_time = time.time()
            func(*args, **kwargs)  # run test1
            stop_time = time.time()
            print('the func time is %s' % (stop_time - start_time))
    
        return warpper
    
    
    @timmer
    def test1(m):
        time.sleep(2)
        print('in the test1')
    
    test1('12')

    这样不管test1有没有参数,有几个参数都能满足需求

    有返回值的装饰器

    还是上面的代码,假如test1里有返回值,如下

     1 import time
     2 
     3 
     4 def timmer(func):
     5     def warpper(*args, **kwargs):
     6         start_time = time.time()
     7         func(*args, **kwargs)  # run test1
     8         stop_time = time.time()
     9         print('the func time is %s' % (stop_time - start_time))
    10 
    11     return warpper
    12 
    13 
    14 @timmer
    15 def test1():
    16     time.sleep(2)
    17     print('in the test1')
    18     return 666
    19 
    20 # test1()
    21 print(test1())

    结果:

    in the test1
    the func time is 2.0003857612609863
    None

    我们可以看到,结果返回了一个None,因为warpper函数的时候,在执行到func(*args, **kwargs)的时候,执行了test1函数,虽然是执行了,但是return返回的值没有接收,那我们可以定义一个变量接收一下,然后再return给调用者

     1 import time
     2 
     3 
     4 def timmer(func):
     5     def warpper(*args, **kwargs):
     6         start_time = time.time()
     7         res = func(*args, **kwargs)  # run test1
     8         stop_time = time.time()
     9         print('the func time is %s' % (stop_time - start_time))
    10         return res
    11 
    12     return warpper
    13 
    14 
    15 @timmer
    16 def test1():
    17     time.sleep(2)
    18     print('in the test1')
    19     return 666
    20 
    21 # test1('12')
    22 print(test1())

    这样的话,我们也实现了一个带返回值的装饰器,如果test1没有返回值,则返回的是None,在看个栗子,加深下理解

    def auth(func):
        def timer(*args, **kwargs):
            print('加的新功能start。。。')
            res = func(*args, **kwargs)  # 将返回值得结果赋给res
            print('加的新功能end。。。')
            return res  # 执行返回值的结果,没有不执行

    return timer @auth # test2=auth(test2) def test2(*args, **kwargs): print('in the test2...') return '我是返回值' print(test2('zou', 'IT', '24'))

    结果:

    加的新功能start。。。
    in the test2...
    加的新功能end。。。
    我是返回值

    实战

     1 user, passwd = 'zou', '123'
     2 
     3 def auth(func):
     4     def timmer(*args, **kwargs):
     5         username = input('输入用户名:')
     6         password = input('输入密码:')
     7         if username == user and password == passwd:
     8             print('加的新功能')
     9             res = func(*args, **kwargs)  # 如果不将结果赋给res就没有返回值
    10             return res
    11         else:
    12             exit('密码错误')
    13     return timmer
    14 
    15 def test1():
    16     print('in the test1')
    17 
    18 
    19 @auth
    20 def test2(name, job):
    21     print('name:%s' % name)
    22     print('job:%s' % job)
    23 
    24 
    25 @auth
    26 def test3():
    27     print('in the test3')
    28     return 'end'
    29 
    30 
    31 test1()
    32 test2('zou', 'IT')
    33 print(test3())

    大家可以自己在脑海里想一下,是怎么运行的,为什么这么运行?最后运行的结果是什么?

    结果:

    in the test1
    输入用户名:zou
    输入密码:123
    加的新功能
    name:zou
    job:IT
    输入用户名:zou
    输入密码:123
    加的新功能
    in the test3
    end
    点我偷看
  • 相关阅读:
    ASP.NET MVC请求处理管道生命周期的19个关键环节(13-19)
    ASP.NET MVC请求处理管道生命周期的19个关键环节(7-12)
    ASP.NET MVC请求处理管道生命周期的19个关键环节(1-6)
    关于领域模型
    WebForm和MVC的一些知识(转)
    抽象工厂
    SCP,scp linux2台机器之间如何传输文件
    mysql卸载(windows)【转】
    (5.2.3)配置服务器参数——服务器性能估算
    Windows命令行使用FTP
  • 原文地址:https://www.cnblogs.com/zouzou-busy/p/11181052.html
Copyright © 2011-2022 走看看