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

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

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

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

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

            (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.上邊實現了一個簡單的可以裝飾不帶參數的裝飾器,但在正常狀況下,咱們的函數都須要傳入適當的參數,如何可以實現對有參數的方法進行裝飾呢?utf-8

#!/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.上邊的裝飾器已經能夠實現基本要求,便可以完成對指定的函數添加附加功能的做用,可是在某些特定時候,咱們須要裝飾器自身也帶上參數,如何實現呢?it

#!/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
'''

   是否是一樣的很簡單呢? class

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

#!/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)   #這裏就等同於@simple_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))))
相關文章
相關標籤/搜索