1. 程式人生 > >對Python中裝飾器(Decorator)的理解與進階

對Python中裝飾器(Decorator)的理解與進階

python decorator 裝飾器

有時候我們項目中的某些功能做些修改即需要對內部的某些函數添加一些附加功能,但是為了安全起見不想改變函數的源代碼以及函數的調用方式,那麽裝飾器在這個地方會給我們帶來很大的幫助。

裝飾器(Decorator):(又叫語法糖)

定義:本質是函數,功能(裝飾其它函數)就是為其他函數添加附加功能

原則:(1).不能修改被裝飾的函數的源代碼

(2).不能修改被裝飾的函數的調用方式

1.先來實現一個簡單的裝飾器示例:

#!/usr/bin/env python
# -*- coding:utf-8 -*-
#定義一個簡單的裝飾器
def simple_wrapper(func):
    def wrapper():
        print("我是裝飾器,我用來裝飾%s" % func.__name__)
        func()
    return wrapper
#需要裝飾的函數
@simple_wrapper
def say_hello():
    print("Hello World")
#執行say_hello()函數
say_hello()
‘‘‘運行結果如下:
我是裝飾器,我用來裝飾say_hello
Hello World
‘‘‘

上邊實現了一個簡單的裝飾器,能過用來裝飾不帶參數的函數,通過這個簡單的示例,我們大概對裝飾器的基本實現有一個大概的了解。但是如果想充分了解並掌握裝飾的原理必要還要對python中的高階函數、嵌套函數、以及函數即“變量”等概念有一定的了解和掌握。下邊我會對裝飾器以及相關的內容進行舉例說明。

2.上邊實現了一個簡單的能夠裝飾不帶參數的裝飾器,但在正常情況下,我們的函數都需要傳入適當的參數,如何能夠實現對有參數的方法進行裝飾呢?

#!/usr/bin/env python
# -*- coding:utf-8 -*-
def simple_wrapper(func):
    def wrapper(*args, **kwargs):
        print("我是裝飾器,我用來裝飾%s方法" % func.__name__)
        func(*args, **kwargs)
    return  wrapper
@simple_wrapper
def say_hello(name):
    print("Hello",name)
#執行say_hello()函數
say_hello("Jack")
‘‘‘運行結果:
我是裝飾器,我用來裝飾say_hello方法
Hello Jack
‘‘‘
#是不是同樣很簡單呢? 這種方式既可以裝飾帶任意參數的函數,也可以裝飾不帶參數的函數。

3.上邊的裝飾器已經可以實現基本要求,即可以完成對指定的函數添加附加功能的作用,但是在某些特定時候,我們需要裝飾器自身也帶上參數,如何實現呢?

#!/usr/bin/env python
# -*- coding:utf-8 -*-
def my_wrapper(args):
    print("我的參數是:",args)
    def simple_wrapper(func):
        def wrapper(*args, **kwargs):
            print("我是裝飾器,我用來裝飾%s方法" % func.__name__)
            func(*args, **kwargs)
        return  wrapper
    return simple_wrapper
    
@my_wrapper("simple")
def say_hello(name):
    print("Hello",name)
#執行say_hello()函數
say_hello("Jack")
‘‘‘運行結果:
我的參數是: simple
我是裝飾器,我用來裝飾say_hello方法
Hello Jack
‘‘‘

是不是同樣的很簡單呢?

簡單是簡單,但是關鍵裝飾器是怎麽實現對其它函數添加附加功能呢?上邊的函數是如何運行的呢?下面請看下邊的簡單示例:

#!/usr/bin/env python
# -*- coding:utf-8 -*-
def simple_wrapper(func):
    def wrapper(*args, **kwargs):
        print("我是裝飾器,我用來裝飾%s方法" % func.__name__)
        func(*args, **kwargs)
    return  wrapper
    
def say_hello(name):
    print("Hello",name)
    
say_hello = simple_wrapper(say_hello)   [email protected]_wrapper的作用
#其實這裏相當於say_hello=wrapper
say_hello("Jack")        #所有在執行say_hello("Jack"),就相當調用了wrapper("Jack")函數

簡單解釋:可以看到上邊也能實現了裝飾器的功能,對該示例簡要分析:因為在python中函數作為一個對象但也可以看成一種“變量”,不僅可以將函數名拿來賦值給其它變量,也可以將函數名當做參數傳遞給其它函數,並且還可以將函數名作為返回值。(通俗點可以這樣說,就是函數名在內存中就是一個內存地址,它指向函數體的內存地址空間,所以可以將函數名當做一個“變量”來進行相關操作,單當在函數名"變量"後邊加上()的時候它就變成了函數調用,會去執行函數體。)在看上邊的小例子,當執行say_hello = simple_wrapper(say_hello) 這一步的時候,將函數名say_hello當做參數傳給了函數simple_wrapper(),但是接著函數simple_wrapper()將內部函數wrapper作為返回值返回,然後say_hello“被重新復制”,即say_hello指向了wrapper函數體的內存空間,接著當執行say_hello("Jack")的時候就相當於執行wrapper("Jack")。

4.通過上邊的示例我們會發現當使用裝飾器的時候,函數say_hello其實被simple_wrapper取代了,理所當然它的__name__等信息都變成了simple_wrapper函數的信息。如何既能讓函數被裝飾,又不改變其自身的信息呢?其實在Python裏提供了一個functools.wraps,而wraps也是一個裝飾器,它作用就是實現這種功能的。

#!/usr/bin/env python
# -*- coding:utf-8 -*-
‘‘‘
def simple_wrapper(func):
    def wrapper(*args, **kwargs):
        print("我是裝飾器,我用來裝飾%s方法" % func.__name__)
        func(*args, **kwargs)
    return  wrapper
@simple_wrapper
def say_hello(name):
    print("Hello",name)
#輸出say_hello現在的__name___屬性
print(say_hello.__name__)  #輸出結果為:wrapper
‘‘‘
######使用functools.wraps
from functools import wraps
def simple_wrapper(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print("我是裝飾器,我用來裝飾%s方法" % func.__name__)
        func(*args, **kwargs)
    return  wrapper
@simple_wrapper
def say_hello(name):
    print("Hello",name)
#輸出say_hello現在的__name__屬性
print(say_hello.__name__)  #輸出結果為:say_hello

5.其實一個函數可以同時定義多個裝飾器,當一個函數定義多個裝飾器時,裝飾器的執行順序是先執行最裏層的裝飾器最後調用最外層的裝飾器,簡單示例:

@a
@b
@c
def say_hello():
   pass
#該示例中裝飾器的執行順序是c > b > c(等效於a(b(c(say_hello))))


對Python中裝飾器(Decorator)的理解與進階