1. 程式人生 > >Python各種型別裝飾器詳解說明

Python各種型別裝飾器詳解說明

裝飾器說明

  • Python中的裝飾器是一種可以裝飾其它物件的工具。
  • 該工具本質上是一個可呼叫的物件(callable),所以裝飾器一般可以由函式、類來實現。
  • 裝飾器本身需要接受一個被裝飾的物件作為引數,該引數通常為函式、方法、類等物件。
  • 裝飾器需要返回一個物件,該物件可以是 經過處理的原引數物件、一個包裝且類似原引數的物件;或者返回一個不相干內容(通常不建議使用)

相信通過上述一段文字的描述,大家應該更加的迷惑了!所以下面我們就結合程式碼來理解Python中的裝飾器。

裝飾器分類

最簡單的裝飾器

def warp(obj):
    return obj

沒錯!!!這就是最簡單的裝飾器,並且是一個沒有任何用處的裝飾器。但是它確實是一個裝飾器,並且可以用的很好。比如:

@warp    # 等價於 foo = warp(foo)
def foo():    
    print('hello decorator!')

foo()    # => hello decorator!

而上面使用了裝飾器的程式碼,其實我們可以通過其它方式達到相同的效果。具體見下:

def foo():
    print('hello decorator!')

foo = warp(foo)
foo()    # => hello decorator!

So,通過最簡單的程式碼,我們可以發現裝飾器其實就是接受了一個函式(物件),並且返回了一個函式(物件)的函式(可呼叫物件)。

用於修改物件的裝飾器

在理解了裝飾器的含義之後,再來看一個稍微有點作用的裝飾器。程式碼如下:

def warp(obj):
    obj.name = 'python'
    return obj

這個裝飾器在上一個例子的基礎上,只添加了一行程式碼,但是卻有了實際的作用。它的作用就是給被裝飾的物件,新增一個name屬性並且設定值為python。這個裝飾器的使用效果如下:

@warp        # => Bar = warp(Bar)
class Bar(object):
    def __init__(self):
        pass

print(Bar.name)     # => python

可以看到實際的使用過程中,warp裝飾器已經成功的給Bar物件添加了name屬性。除了給類物件新增屬性之外,它還可以給函式物件新增屬性。

@warp       # => foo = warp(foo)
def foo():
    pass

print(foo.name)         # => python

用於模擬物件的裝飾器--函式裝飾器

上面例子中的裝飾器,是直接修改了傳入物件;而裝飾器最常用的方式卻是模擬一個傳入物件。即返回一個和原物件相似的物件(即呼叫介面完全一樣的另一個物件),並且該模擬物件是包裝了原物件在內的。具體程式碼如下:

def outer(func):         # 函式裝飾器
    def inner():
        func()

    return inner

上面是一個函式裝飾器,即用來修飾函式的裝飾器。因為它返回了一個模擬func物件的inner物件。而這裡inner物件是一個函式,所以這個裝飾器只能裝飾函式。(因為inner物件只能模擬func這樣的函式物件,不能模擬class物件)

@outer      # foo = outer(foo)
def foo():
    print('hello foo')

foo()    # => hello foo

上述程式碼中最後一行foo(),其實質上是執行的inner()。為了證明這一點,我們可以在inner中列印一條資訊。並檢視下foo的__name__屬性。

def outer(func):         # 函式裝飾器
    def inner():
        print('hello inner')
        func()

    return inner

@outer      # foo = outer(foo)
def foo():
    print('hello foo')

print(foo.__name__)
foo()

上述程式碼執行後的結果如下:

inner
hello inner
hello foo

可以看到首先列印的是 foo.__name__程式碼,注意內容是inner而不是foo(說明其本質上是inner函式);其次列印的時候,先列印inner函式中的內容,後列印foo函式中的內容。

用於模擬物件的裝飾器--類方法裝飾器

與函式裝飾器類似的還有類方法裝飾器,其作用相同,格式相近。只是些微有些區別,下面就是類方法裝飾器的程式碼。

def outer(obj):         # 類方法裝飾器
    def inner(self):
        print('hello inner')
        obj(self)

    return inner

class Zoo(object):
    def __init__(self):
        pass

    @outer        # => zoo = outer(zoo)
    def zoo(self):
        print('hello zoo')

zoo = Zoo()
print(zoo.zoo.__name__)
zoo.zoo()

可以看到類方法裝飾器和函式裝飾器,唯一的區別就是多了一個預設的self引數;這是因為類方法本身就比函式多這麼一個引數。其執行的結果如下:

inner
hello inner
hello zoo

所以最後一行程式碼zoo.zoo函式執行的其實是inner函式。

用於模擬物件的裝飾器--類裝飾器

裝飾器除了可以裝飾函式、方法之外,還可以裝飾器類物件。具體的程式碼如下:

def outer(clss):         # 類裝飾器
    class Inner(object):
        def __init__(self):
            self.clss = clss()

        def __getattr__(self, attr):
            return getattr(self.clss, attr)

    return Inner


@outer          # Zoo = outer(Zoo)
class Zoo(object):
    def __init__(self):
        pass

    def say(self):
        print('hello world!')

zoo = Zoo()
print(zoo.__class__)    # <class '__main__.outer.<locals>.Inner'>
zoo.say()               # hello world!

通過程式碼可以看出,類裝飾器與函式裝飾器類似。即模擬一個與原引數介面一致的類物件。所以對於模擬類的裝飾器,只能用在其可以模擬的物件之上,並不能互相修飾其它型別的物件。

特殊應用的裝飾器

上面都是比較常規的裝飾器,python中還有另外一些特殊的裝飾器。比如:類靜態屬性裝飾器。比如下面的程式碼:

class Foo(object):
    def __init__(self, height, weigth):
        self.height = height
        self.weigth = weigth

    @property
    def ratio(self):
        return self.height / self.weigth

foo = Foo(176, 120)
print(foo.ratio)    # => 1.4666666666666666

上述程式碼中的@property裝飾器就是一個特殊的裝飾器,它把ratio方法變成了一個屬性。從最後一句呼叫程式碼可以看出,使用的是foo.ratio而不是foo.ratio()。

對於這類裝飾器需要Python的特定屬性和機制的支援才可以實現,不同特性的裝飾器所需機制不同。如上述程式碼中的@property裝飾器就可以使用下面的程式碼來實現。

class Prop(object):
    def __init__(self, fget):
        self.fget = fget

    def __get__(self, instance, owner):
        return self.fget(instance)

具體的使用效果如下:

class Foo(object):
    def __init__(self, height, weigth):
        self.height = height
        self.weigth = weigth

    @Prop
    def ratio(self):
        return self.height / self.weigth

foo = Foo(176, 120)
print(foo.ratio)    # => 1.4666666666666666

可以看到效果和原生的@property裝飾器是一樣的。

類實現的裝飾器

在之前對於裝飾器的說明中,有說道裝飾器是一個callable物件。除了函式可以實現裝飾器之外,還可以通過類來實現。那麼類實現裝飾器的具體程式碼如下:

class Warp(object):
    def __init__(self):
        pass

    def __call__(self, obj):
        obj.name = 'warp'
        return obj

這個類裝飾器實現的功能,也是給傳入的物件新增name屬性,並設定其值為warp。其呼叫效果如下:

@Warp()
def foo():
    pass

print(foo.name)    # => warp

裝飾帶引數/返回值的物件

前面列舉的所有例子,被裝飾的物件都是無引數的。如果你需要裝飾一個帶引數的物件。那麼就需要響應的修改下裝飾器程式碼了。注意:這裡特指那些模擬型別的裝飾器。即函式裝飾器、類方法裝飾器、類裝飾器。

假設我們先有一個帶引數的函式,其內容如下:

def add(x, y):
    return x * y

如果使用原來的函式裝飾器,肯定就會出錯。主要因為這個函式帶引數,並且也有返回值。而原來的函式裝飾器則不能支援,原函式裝飾器如下:

def outer(func):         # 函式裝飾器
    def inner():
        func()

    return inner

可以看到inner模擬的僅僅是一個無引數、無返回值的物件。所以需要進行如下的修改:

def outer(func):         # 函式裝飾器
    def inner(x, y):
        print('hello inner')
        return func(x, y)

    return inner

這樣的函式裝飾器就可以裝飾add函數了。因為inner函式添加了x,y引數,呼叫func物件時也添加了引數,並且返回了func物件的返回值。具體使用效果如下:

@outer
def add(x, y):
    return x * y

print(add(2, 3))    # => 6

上述程式碼雖然可以實現add的裝飾功能,但是如果現在我們在出現一個三個引數的函式需要裝飾,或者一個帶預設值引數的韓式需要裝飾怎麼辦。我們不可能為沒一個不同引數的函式都寫一個相同功能的裝飾器。所以終極的函式裝飾器的寫法如下:

def outer(func):         # 函式裝飾器
    def inner(*args, **kwargs):
        print('hello inner')
        return func(*args, **kwargs)

    return inner

這裡使用了python中動態引數的概念,這樣裝飾器就可以支援任意的組合引數的函數了。

裝飾器帶引數

上面說到的是被修飾的物件帶引數的情況,還有一種情況就是裝飾器本身希望支援帶引數。這種情況類似於函式模組通過帶引數可以更加靈活的道理一樣。通過給裝飾器帶上引數,可以使得裝飾器的功能更加的靈活。程式碼如下:

url_mapping = {}

def route(url):
    def decorator(func):         # 函式裝飾器
        url_mapping[url] = func
        return func
    return decorator

上面是一個URL路由對映的裝飾器,可以給不同的函式繫結不同的路由。如果裝飾器不能帶引數,則無法實現這樣的功能。其使用效果如下:

@route('/home')
def home():
    pass
    
@route('/index')
def index():
    pass
    
print(url_mapping)  # => {'/home': <function home at 0x01DAD810>, '/index': <function index at 0x01DAD7C8>}

裝飾器應用

Python裝飾器的應用比較廣泛,大部分場景的公共處理邏輯都可以使用裝飾器去簡化。(使用上類似於JAVA中的註解)一般比較常見的場景比如:

  • 日誌記錄
  • 許可權驗證
  • 單例模式
  • 競爭資源管理

相關推薦

Python各種型別裝飾說明

裝飾器說明 Python中的裝飾器是一種可以裝飾其它物件的工具。 該工具本質上是一個可呼叫的物件(callable),所以裝飾器一般可以由函式、類來實現。 裝飾器本身需要接受一個被裝飾的物件作為引數,該引數通常為函式、方法、類等物件。 裝飾器需要返回一個物件,該物件可

python--裝飾

blog 內容 class align fun turn strip 叠代器 ros Python---裝飾器詳解 定義: 本質上是一個函數。作用是用來裝飾另一個函數(即被裝飾函數),給被裝飾函數添加功能。前提是不能改變被裝飾函數的源代碼和調用方式。這樣的一個函數稱之為裝飾

Python裝飾

def 功能 style out return 裝飾器 代碼 方法 情況 首先是不使用裝飾器的情況,又需要在不修改原函數的情況話修改函數結果 1 def outer(func): 2 def inner(): 3 print("Hello")

Python裝飾 Python裝飾

詳解Python的裝飾器   Python中的裝飾器是你進入Python大門的一道坎,不管你跨不跨過去它都在那裡。 為什麼需要裝飾器 我們假設你的程式實現了say_hello()和say_goodbye()兩個函式。 def say_hello():

python~裝飾

裝飾器的概念 在程式碼執行期間在不改變原函式定義的基礎上,動態給該函式增加功能的方式, 稱之為裝飾器。 裝飾器是一個很著名的設計模式,較為經典的有插入日誌、效能測試、事務處理等。裝飾器是解決這類問題的絕佳設計,有了裝飾器,我們就可以抽離出大量函式中與函式功能本身無關的雷同程式碼並繼續重用

python裝飾

裝飾器 我們知道,在python中,我們可以像使用變數一樣使用函,這主要依賴於以下幾點: 函式可以被賦值給其他變數 函式可以被刪除 可以在函式裡面再定義函式,函式巢狀。 函式可以作為引數傳遞給另外一個函式 函式可以作為另一個函式的返回值 對一個簡單的函

python裝飾(三)---裝飾高階用法

1. 在裝飾器函式裡傳入引數def a_decorator_passing_arguments(function_to_decorate):    def a_wrapper_accepting_arguments(arg1,arg2):        print("Igot

python 裝飾

不定 返回 緩存 特性 mef ali int 感受 cti 裝飾器 裝飾器其實就是一個閉包,把一個函數當做參數然後返回一個替代版函數 裝飾器有2個特性: 一是可以把被裝飾的函數替換成其他函數,  二是可以在加載模塊時候立即執行 def w1(func):   

裝飾

其他 告訴 原函數 基於 from 完全 property in out 語法糖 Python中的裝飾器是你進入Python大門的一道坎,不管你跨不跨過去它都在那裏。 為什麽需要裝飾器 我們假設你的程序實現了say_hello()和say_goodbye()兩個函數

2018.10.27PYTHON_DJANpython裝飾GO_CLASS118~119

#CLASS118 用表單驗證資料 常用的Field: 使用Field可以是對資料驗證的第一步。你期望這個提交上來的資料是什麼型別,那麼就使用什麼型別的Field。 CharField: 用來接收文字。

Python高階程式設計——裝飾Decorator(上篇)(絕對是我見過最詳細的的教程,沒有之一哦)

一、先從一種情況開始看起 1、裝飾器decorator的由來 裝飾器的定義很是抽象,我們來看一個小例子。 先定義一個簡單的函式: def myfunc:     print('我是函式myfunc') myfunc() #呼叫函式 然後呢,我想看看

python裝飾

python裝飾器裝飾器作為python的中的一個功能可以說是非常重要的,也是學習python中的一個門坎,可能很多人會覺得在學習裝飾器的時候很容易會搞迷糊,最在看python核心編程時和python之闡時感覺有點明白了,在此拋磚引玉。 裝飾器的作用最大的特點的是,在不修改原代碼的情況下,給原有的代碼附加功能

python 基本資料型別--字串例項

  字串(str) :把字元連成串. 在python中⽤', ", ''', """引起來的內容被稱為字串 。   注意:python中沒有單一字元說法,統一稱叫字串。 一、切片和索引   1、索引:索引就是下標,從0開始      str= "我是字串" print("str[0]=",st

python迭代

迭代器 迭代是訪問集合元素的一種方式。迭代器是一個可以記住遍歷的位置的物件。迭代器物件從集合的第一個元素開始訪問,直到所有的元素被訪問完結束。迭代器只能往前不會後退。 1. 可迭代物件 我們已經知道可以對list、tuple、str等型別的資料使用for...in...的迴圈語法從其中依次拿到資料進行使

Python零基礎入門之函式的修飾

內嵌函式 要理解修飾器,首先要知道python的內嵌函式。 在函式內部可以建立另外一個函式,不過內部函式也只能在外部函式的作用域之內呼叫才有效。 如果內部函式定義中包含了外部函式定義的物件的引用,內部函式會被稱為閉包 私信小編007即可獲取小編精心準備的大禮包一份哦!

Python Tkinter Grid佈局管理

Grid(網格)佈局管理器會將控制元件放置到一個二維的表格裡。主控制元件被分割成一系列的行和列,表格中的每個單元(cell)都可以放置一個控制元件。 什麼時候使用Grid管理器 grid管理器是Tkinter裡面最靈活的幾何管理佈局器。如果你不確定什麼情況

Python學習——for迴圈,生成器,迭代

文章目錄 Python的for迴圈 for迴圈示例 List 列表迴圈 dict 字典迴圈 列表生成式 生成器 列表式生成器 函式式生成器 生成器式生產者消費

使用Pytorch訓練分類(附python演練)!

【前言】:你已經瞭解瞭如何定義神經網路,計算loss值和網路裡權重的更新。現在你也許會想資料怎麼樣? 目錄: 一.資料 二.訓練一個影象分類器 使用torchvision載入並且歸一化CIFAR10的訓練和測試資料集 定義一個卷積神經網路 定義一個損失函式 在

python 類內部裝飾的實現 與 引數構學習

學習了函式的裝飾器的寫法,然後想到如果要在類中初始化或獲取資訊時能用裝飾器做過濾和驗證應該怎麼寫呢, 在網上查了下相關資訊,感覺這樣也是可以的,不知道會不會有什麼問題class Ctj(): class Ctj(): sex = 'man' name = 'name' age

自然語言處理之中文分詞-jieba分詞python實戰

中文分詞是中文文字處理的一個基礎步驟,也是中文人機自然語言互動的基礎模組,在進行中文自然語言處理時,通常需要先進行分詞。本文詳細介紹現在非常流行的且開源的分詞器結巴jieba分詞器,並使用python實