美文网首页
Python中的装饰器

Python中的装饰器

作者: Techml | 来源:发表于2017-05-02 19:23 被阅读0次

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

    装饰器原则:

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

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

    装饰器=高阶函数 + 函数嵌套 +闭包

    1.高阶函数

    定义:

    1. 函数接收的参数是一个函数名

    2. 函数的返回值是一个函数名

    满足上述任意一个条件的函数即为高阶函数

    高阶函数示例

     1 def foo():
     2     print("hello")
     3 
     4 
     5 # 函数名作为参数
     6 def func_1(func):
     7     print("heiheihei")
     8     func()
     9     print("world")
    10 
    11 
    12 # 函数名作为返回值
    13 def func_2(func):
    14     print("return value: %s" % func)
    15     return func
    16 
    17 # 函数名作为参数和返回值
    18 def func_3(func):
    19     print("from func_3")
    20     return func
    21 
    22 func_1(foo)
    23 print('----------------')
    24 func_2(foo)
    25 print('----------------')
    26 f = func_3(foo)
    27 f() # f是func_3的返回值,即为函数foo的函数地址,f()执行函数foo输出“hello”
    28 
    29 >>heiheihei
    30 hello
    31 world
    32 ----------------
    33 return value: <function foo at 0x00000276CAAA3E18>
    34 ----------------
    35 from func_3
    36 hello
    

    使用高阶函数实现在不改变函数调用方式的前提下,增加函数的功能

     1 import time
     2 
     3 # 为foo函数增加函数运行时间计时功能
     4 
     5 def foo():
     6     time.sleep(2)
     7     print("function foo running")
     8 
     9 def timer(func):
    10     start_time = time.time()
    11     func()
    12     stop_time = time.time()
    13 
    14     print("running time %s" % (start_time - stop_time))
    15 
    16     return func
    17 
    18 foo = timer(foo)
    19 
    20 foo()
    21 
    22 >>function foo running
    23 running time -2.0002999305725098
    24 function foo running    
    25 
    26 # 以foo()的形式运行函数foo,实现了不改变函数调用方式运行函数
    27 # 但运行过程中多执行了一次foo,
    28 # 第一次执行是timer函数中的func()
    29 # 第二次执行是timer函数返回了foo函数名,最后运行foo()再次运行了foo原函数
    

    根据以上示例可以得出单纯使用高阶函数无法完全实现在不改变函数调用方式的基础下增加函数的功能

    2. 函数嵌套

    定义:在函数里面嵌套定义函数

     1 def country(name):
     2     print('country is %s' %name)
     3     def province():
     4         print('guangdong')
     5         def city():
     6             print('shenzhen')
     7         city()
     8     province()
     9 
    10 country('china')
    11 
    12 >>country is china
    13 guangdong
    14 shenzhen
    

    3. 闭包

    定义:如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包(closure)

     1 def country(name):
     2     print('country is %s' %name)
     3     def province():
     4         print('province is %s' % name)
     5         def city():
     6             print('city is %s' % name)
     7         city()
     8     province()
     9 
    10 country('china')
    

    province()函数就是一个闭包

    4. 使用高阶函数+函数嵌套+闭包实现为函数增加功能

    import time
    
    def timer(func):
        def inner():
            start_time = time.time()
            func()
            stop_time = time.time()
            print("running time is %s" % (stop_time- start_time))
        return inner
    
    def foo():
        print("foo function")
        time.sleep(2)
    
    f = timer(foo) # f就是timer函数的返回值--inner函数的函数指针
    
    f() # f加括号执行f函数
    
    >>foo function
    running time is 2.000217914581299
    

    上述示例实现了为foo函数增加函数运行时间计时的功能,如果将f改为foo,则实现了不改变函数的调用方式同时增加了函数的功能,如下所示

     1 import time
     2 
     3 def timer(func):
     4     def inner():
     5         start_time = time.time()
     6         func()
     7         stop_time = time.time()
     8         print("running time is %s" % (stop_time- start_time))
     9     return inner
    10 
    11 def foo():
    12     print("foo function")
    13     time.sleep(2)
    14 
    15 foo = timer(foo) 
    16 
    17 foo() 
    18 
    19 >> foo function
    20 running time is 2.000906229019165
    21 
    22 # 最后执行的foo函数相比于最初定义的foo函数,实现了增加函数运行时间计时的功能
    

    到此基本实现了在不改变函数的调用方式以及函数源码的前提下,实现了增加函数功能,但还需要做一步函数赋值的操作,如果其他函数需要怎加同样的功能,还要对其他函数做赋值操作,这是很麻烦的。

    使用装饰器可以避免这样的麻烦,只要在添加功能的目标函数前添加一行代码:@功能函数。实现方法如下所示

     1 import time
     2 
     3 def timer(func):
     4     def inner():
     5         start_time = time.time()
     6         func()
     7         stop_time = time.time()
     8         print("running time is %s" % (stop_time- start_time))
     9     return inner
    10 
    11 @timer
    12 def foo():
    13     print("foo function")
    14     time.sleep(2)
    15 
    16 foo()
    17 
    18 >>foo function
    19 running time is 2.000242233276367
    

    5. 获取被装饰函数的返回值

     1 import time
     2 
     3 def timer(func):
     4     def inner():
     5         start_time = time.time()
     6         ret = func() # 执行func函数得到函数返回值,赋值给ret
     7         stop_time = time.time()
     8         print("running time is %s" % (stop_time- start_time))
     9         return ret # inner的返回值为ret
    10     return inner # timer的返回值为inner函数名
    11 
    12 @timer
    13 def foo():
    14     print("foo function")
    15     time.sleep(2)
    16     return "return value foo"
    17 
    18 ret = foo()
    19 # 首先假设foo = A
    20 # 执行foo() <==> 首先执行A = timer(foo),然后执行A()
    21 # timer(foo)的返回值为inner,即A = inner
    22 # 执行A() <==> 执行inner()
    23 # 执行inner()过程中执行inner函数体中的所有代码,得到最初定义的foo函数的返回值“return value foo”
    24 # 即执行A()得到返回值“return value foo”
    25 # ret = A() 将A()的返回值赋值给ret, 即使用ret=foo()可以得到最初定义的foo函数的返回值
    26 print(ret)
    27 
    28 >>foo function
    29 running time is 2.0001463890075684
    30 return value foo
    

    6. 被修饰函数有参数

     1 import time
     2 
     3 def timer(func):
     4     def inner(*args, **kwargs):
     5         start_time = time.time()
     6         ret = func(*args, **kwargs) # 执行func函数得到函数返回值,赋值给ret
     7         stop_time = time.time()
     8         print("running time is %s" % (stop_time- start_time))
     9         return ret # inner的返回值为ret
    10     return inner # timer的返回值为inner函数名
    11 
    12 @timer
    13 def foo(name):
    14     print("usr name is %s" % name)
    15     time.sleep(2)
    16     return "return value foo"
    17 
    18 ret = foo("Jack")
    19 # 首先假设foo = A
    20 # 执行foo() <==> 首先执行A = timer(foo),然后执行A()
    21 # timer(foo)的返回值为inner,即A = inner
    22 # 执行A() <==> 执行inner()
    23 # 执行inner()过程中执行inner函数体中的所有代码,
    24 # 执行到func函数时,即执行被修饰函数foo(name),foo有参数,则func也需要参数
    25 # func的参数需要从A()的参数中获得,即inner的参数就是func的参数
    26 # 由于参数是不定的,可以为函数添加参数 *args, **kwargs
    27 
    28 print(ret)
    29 
    30 >> usr name is Jack
    31 running time is 2.0003693103790283
    32 return value foo
    

    7. 带参数装饰器

     1 import time
     2 
     3 def type_check(*args, **kwargs):
     4     file_type = kwargs["file_type"]
     5     print("file type %s" %file_type)
     6 
     7     def timer(func):
     8         def inner(*args, **kwargs):
     9             start_time = time.time()
    10             ret = func(*args, **kwargs) # 执行func函数得到函数返回值,赋值给ret
    11             stop_time = time.time()
    12             print("running time is %s" % (stop_time- start_time))
    13             return ret # inner的返回值为ret
    14         return inner # timer的返回值为inner函数名
    15 
    16     return timer
    17 
    18 @type_check(file_type = "mysql")
    19 def foo(name):
    20     print("usr name is %s" % name)
    21     time.sleep(2)
    22     return "return value foo"
    23 
    24 ret = foo("Jack")
    25 # type_check加括号首先执行type_check(),获取type_check的参数
    26 # foo <==> A = type_check(), A得到timer返回值
    27 # 执行foo() <==> timer()
    28 # 相比于无参装饰器,有参装饰器在无参装饰器的基础上套一层函数来传参
    29 
    30 print(ret)
    31 
    32 >>file type mysql
    33 usr name is Jack
    34 running time is 2.00007963180542
    35 return value foo
    

    8. 多个装饰器

     1 def outer_0(func):
     2     def inner(*args, **kwargs):
     3         print('3.5')
     4         ret = func(*args, **kwargs)
     5         return ret
     6     return inner
     7 
     8 def outer(func):
     9     def inner(*args, **kwargs):
    10         print('123')
    11         ret = func(*args, **kwargs)
    12         print('456')
    13         return ret
    14     return inner
    15 
    16 @outer_0
    17 @outer
    18 def index(a1, a2):
    19     print('complex')
    20     return a1 + a2
    21 
    22 print(index(1, 2))
    23 
    24 
    25 >>3.5
    26 >>123
    27 >>complex
    28 >>456
    29 >>3
    

    相关文章

      网友评论

          本文标题:Python中的装饰器

          本文链接:https://www.haomeiwen.com/subject/xhlhtxtx.html