最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

深入了解和應(yīng)用Python 裝飾器 @decorator

 更新時間:2019年04月02日 14:26:34   作者:呆木木人兒  
在編程過程中,經(jīng)常遇到這樣的場景:登錄校驗,權(quán)限校驗,日志記錄等,這些功能代碼在各個環(huán)節(jié)都可能需要,但又十分雷同,通過裝飾器來抽象、剝離這部分代碼可以很好解決這類場景,這篇文章主要介紹了Python的裝飾器 @decorator,探討了使用的方式,需要的朋友可以參考下

Python的裝飾器(decorator)是一個很棒的機(jī)制,也是熟練運用Python的必殺技之一。裝飾器,顧名思義,就是用來裝飾的,它裝飾的是一個函數(shù),保持被裝飾函數(shù)的原有功能,再裝飾上(添油加醋)一些其它功能,并返回帶有新增功能的函數(shù)對象,所以裝飾器本質(zhì)上是一個返回函數(shù)對象的函數(shù)(確切的說,裝飾器應(yīng)該是可調(diào)用對象,除了函數(shù),類也可以作為裝飾器)。

在編程過程中,我們經(jīng)常遇到這樣的場景:登錄校驗,權(quán)限校驗,日志記錄等,這些功能代碼在各個環(huán)節(jié)都可能需要,但又十分雷同,通過裝飾器來抽象、剝離這部分代碼可以很好解決這類場景。

裝飾器是什么?

要理解Python的裝飾器,首先我們先理解一下Python的函數(shù)對象。我們知道,在Python里一切都是對象,函數(shù)也不例外,函數(shù)是第一類對象(first-class objects),它可以賦值給變量,也可以作為list的元素,還可以作為參數(shù)傳遞給其它函數(shù)。

函數(shù)可以被變量引用

定義一個簡單的函數(shù):

def say_hi():
 print('Hi!')
say_hi()
# Output: Hi!

個變量say_hi2來引用say_hi函數(shù):

say_hi2 = say_hi
print(say_hi2)
# Output: <function say_hi at 0x7fed671c4378>

say_hi2()
# Output: Hi!

上面的語句中say_hi2 和 say_hi 指向了同樣的函數(shù)定義,二者的執(zhí)行結(jié)果也相同。

函數(shù)可以作為參數(shù)傳遞給其它函數(shù)

def say_more(say_hi_func):
 print('More')
 say_hi_func()

say_more(say_hi)
# Output:
# More
# Hi

在上面的例子中,我們把say_hi函數(shù)當(dāng)做參數(shù)傳遞給say_more函數(shù),say_hi 被變量 say_hi_func 引用。

函數(shù)可以定義在其它函數(shù)內(nèi)部

def say_hi():
 print('Hi!')
 def say_name():
 print('Tom')
 say_name()

say_hi()
# Output:
# Hi!
# Tom

say_name() # 報錯

上述代碼中,我們在say_hi()函數(shù)內(nèi)部定義了另外一個函數(shù)say_name()。say_name()只在say_hi函數(shù)內(nèi)部可見(即,它的作用域在say_hi函數(shù)內(nèi)部),在say_hi外包調(diào)用時就會出錯。

函數(shù)可以返回其它函數(shù)的引用

def say_hi():
 print('Hi!')
 def say_name():
 print('Tom')
 return say_name

say_name_func = say_hi()
# 打印Hi!,并返回say_name函數(shù)對象
# 并賦值給say_name_func

say_name_func()
# 打印 Tom

上面的例子,say_hi函數(shù)返回了其內(nèi)部定義的函數(shù)say_name的引用。這樣在say_hi函數(shù)外部也可以使用say_name函數(shù)了。

前面我們理解了函數(shù),這有助于我們接下來弄明白裝飾器。

裝飾器(Decorator)

裝飾器是可調(diào)用對象(callable objects),它用來修改函數(shù)或類。
可調(diào)用對象就是可以接受某些參數(shù)并返回某些對象的對象。Python里的函數(shù)和類都是可調(diào)用對象。

函數(shù)裝飾器,就是接受函數(shù)作為參數(shù),并對函數(shù)參數(shù)做一些包裝,然后返回增加了包裝的函數(shù),即生成了一個新函數(shù)。

讓我們看看下面這個例子:

def decorator_func(some_func):
 # define another wrapper function which modifies some_func
 def wrapper_func():
 print("Wrapper function started")
 
 some_func()
 
 print("Wrapper function ended")
 
 return wrapper_func # Wrapper function add something to the passed function and decorator returns the wrapper function
 
def say_hello():
 print ("Hello")
 
say_hello = decorator_func(say_hello)

say_hello()

# Output:
# Wrapper function started
# Hello
# Wrapper function ended

上面例子中,decorator_func 就是定義的裝飾器函數(shù),它接受some_func作為參數(shù)。它定義了一個wrapper_func函數(shù),該函數(shù)調(diào)用了some_func但也增加了一些自己的代碼。

上面代碼中使用裝飾器的方法看起來有點復(fù)雜,其實真正的裝飾器的Python語法是這樣的:

裝飾器的Python語法

@decorator_func
def say_hi():
 print 'Hi!'

@ 符合是裝飾器的語法糖,在定義函數(shù)say_hi時使用,避免了再一次的賦值語句。
上面的語句等同于:

def say_hi():
 print 'Hi!'
say_hi = decorator_func(say_hi)

裝飾器的順序

@a
@b
@c
def foo():
 print('foo')

# 等同于:
foo = a(b(c(foo)))

帶參數(shù)函數(shù)的裝飾器

def decorator_func(some_func):
 def wrapper_func(*args, **kwargs):
 print("Wrapper function started")
 some_func(*args, **kwargs)
 print("Wrapper function ended")
 
 return wrapper_func

@decorator_func 
def say_hi(name):
 print ("Hi!" + name)

上面代碼中,say_hi函數(shù)帶有一個參數(shù)。通常情況下,不同功能的函數(shù)可以有不同類別、不同數(shù)量的參數(shù),在寫wrapper_func的時候,我們不確定參數(shù)的名稱和數(shù)量,可以通過*args 和 **kwargs 來引用函數(shù)參數(shù)。

帶參數(shù)的裝飾器

不僅被裝飾的函數(shù)可以帶參數(shù),裝飾器本身也可以帶參數(shù)。參考下面的例子:

def use_logging(level):
 def decorator(func):
 def wrapper(*args, **kwargs):
 if level == "warn":
 logging.warn("%s is running" % func.__name__)
 return func(*args)
 return wrapper

 return decorator

@use_logging(level="warn")
def foo(name='foo'):
 print("i am %s" % name)

簡單來說,帶參數(shù)的裝飾器就是在沒有參數(shù)的裝飾器外面再嵌套一個參數(shù)的函數(shù),該函數(shù)返回那個無參數(shù)裝飾器即可。

類作為裝飾器

前面我們提到裝飾器是可調(diào)用對象。在Python里面,除了函數(shù),類也是可調(diào)用對象。使用類裝飾器,優(yōu)點是靈活性大,高內(nèi)聚,封裝性。通過實現(xiàn)類內(nèi)部的__call__方法,當(dāng)使用 @ 語法糖把裝飾器附加到函數(shù)上時,就會調(diào)用此方法。

class Foo(object):
 def __init__(self, func):
 self._func = func

def __call__(self):
 print ('class decorator runing')
 self._func()
 print ('class decorator ending')

@Foo
def say_hi():
 print('Hi!')

say_hi()
# Output:
# class decorator running
# Hi!
# class decorator ending

functools.wraps

使用裝飾器極大地復(fù)用了代碼,但是他有一個缺點就是原函數(shù)的元信息不見了,比如函數(shù)的docstring、__name__、參數(shù)列表,先看看下面例子:

def decorator_func(some_func):
 def wrapper_func(*args, **kwargs):
 print("Wrapper function started")
 some_func(*args, **kwargs)
 print("Wrapper function ended")
 
 return wrapper_func

@decorator_func 
def say_hi(name):
 '''Say hi to somebody'''
 print ("Hi!" + name)

print(say_hi.__name__) # Output: wrapper_func
print(say_hi.__doc__) # Output: None

可以看到,say_hi函數(shù)被wrapper_func函數(shù)取代,它的__name__ 和 docstring 也自然是wrapper_func函數(shù)的了。
不過不用擔(dān)心,Python有functools.wraps,wraps本身也是一個裝飾器,它的作用就是把原函數(shù)的元信息拷貝到裝飾器函數(shù)中,使得裝飾器函數(shù)也有和原函數(shù)一樣的元信息。

from functools import wraps
def decorator_func(some_func):
 @wraps(func)
 def wrapper_func(*args, **kwargs):
 print("Wrapper function started")
 some_func(*args, **kwargs)
 print("Wrapper function ended")
 
 return wrapper_func

@decorator_func 
def say_hi(name):
 '''Say hi to somebody'''
 print ("Hi!" + name)

print(say_hi.__name__) # Output: say_hi
print(say_hi.__doc__) # Output: Say hi to somebody

類的內(nèi)置裝飾器

  • 類屬性@property
  • 靜態(tài)方法@staticmethod
  • 類方法@classmethod

通常,我們需要先實例化一個類的對象,再調(diào)用其方法。

若類的方法使用了@staticmethod或@classmethod,就可以不需要實例化,直接類名.方法名()來調(diào)用。

從使用上來看,@staticmethod不需要指代自身對象的self或指代自身類的cls參數(shù),就跟使用普通函數(shù)一樣。

@classmethod不需要self參數(shù),但第一個參數(shù)必須是指代自身類的cls參數(shù)。如果在@staticmethod中要調(diào)用到這個類的一些屬性方法,只能直接類名.屬性名,或類名.方法名的方式。

而@classmethod因為持有cls參數(shù),可以來調(diào)用類的屬性,類的方法,實例化對象等。

總結(jié)

通過認(rèn)識Python的函數(shù),我們逐步弄清了裝飾器的來龍去脈。裝飾器是代碼復(fù)用的好工具,在編程過程中可以在適當(dāng)?shù)膱鼍坝枚喽嗍褂谩?br />

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Python練習(xí)之操作MySQL數(shù)據(jù)庫

    Python練習(xí)之操作MySQL數(shù)據(jù)庫

    這篇文章主要介紹了Python練習(xí)之操作MySQL數(shù)據(jù)庫,文章通過如何創(chuàng)建MySQL數(shù)據(jù)表?如何向MySQL表中插入數(shù)據(jù)?如何查詢MySQL中的數(shù)據(jù)?的三個問題展開了詳細(xì)的內(nèi)容介紹
    2022-06-06
  • python 實現(xiàn)分組求和與分組累加求和代碼

    python 實現(xiàn)分組求和與分組累加求和代碼

    這篇文章主要介紹了python 實現(xiàn)分組求和與分組累加求和代碼,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-05-05
  • Python中捕捉詳細(xì)異常信息的代碼示例

    Python中捕捉詳細(xì)異常信息的代碼示例

    這篇文章主要介紹了Python中捕捉詳細(xì)異常信息的代碼示例,本文的代碼是從Python 2.7的源碼中得來,可以獲取文件位置、行號、函數(shù)、異常信息等內(nèi)容,需要的朋友可以參考下
    2014-09-09
  • Python實現(xiàn)快速計算詞頻功能示例

    Python實現(xiàn)快速計算詞頻功能示例

    這篇文章主要介紹了Python實現(xiàn)快速計算詞頻功能,結(jié)合實例形式總結(jié)分析了Python使用nltk庫進(jìn)行詞頻計算功能的相關(guān)操作技巧,需要的朋友可以參考下
    2018-06-06
  • 如何將 awk 腳本移植到 Python

    如何將 awk 腳本移植到 Python

    腳本是解決問題的有效方法,而 awk 是編寫腳本的出色語言。它特別擅長于簡單的文本處理,它可以帶你完成配置文件的某些復(fù)雜重寫或目錄中文件名的重新格式化。這篇文章主要介紹了如何把 awk 腳本移植到 Python,需要的朋友可以參考下
    2019-12-12
  • python數(shù)字圖像處理環(huán)境安裝與配置過程示例

    python數(shù)字圖像處理環(huán)境安裝與配置過程示例

    這篇文章主要為大家介紹了python數(shù)字圖像處理環(huán)境安裝與配置過程示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-06-06
  • python添加命令行參數(shù)的詳細(xì)過程

    python添加命令行參數(shù)的詳細(xì)過程

    Click 是 Flask 的開發(fā)團(tuán)隊 Pallets 的另一款開源項目,它是用于快速創(chuàng)建命令行的第三方模塊,這篇文章主要介紹了python怎么添加命令行參數(shù),需要的朋友可以參考下
    2023-06-06
  • python list 切片倒著取的實現(xiàn)示例

    python list 切片倒著取的實現(xiàn)示例

    切片操作非常靈活,可以按照需要獲取列表中的任意一段元素,本文主要介紹了python list 切片倒著取的實現(xiàn)示例,具有一定的參考價值,感興趣的可以了解一下
    2024-01-01
  • Python中使用matplotlib模塊errorbar函數(shù)繪制誤差棒圖實例代碼

    Python中使用matplotlib模塊errorbar函數(shù)繪制誤差棒圖實例代碼

    在matplotlib中,errorbar方法用于繪制帶誤差線的折線圖,下面這篇文章主要給大家介紹了關(guān)于Python中使用matplotlib模塊errorbar函數(shù)繪制誤差棒圖的相關(guān)資料,需要的朋友可以參考下
    2022-08-08
  • Python基于callable函數(shù)檢測對象是否可被調(diào)用

    Python基于callable函數(shù)檢測對象是否可被調(diào)用

    這篇文章主要介紹了Python基于callable函數(shù)檢測對象是否可被調(diào)用,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-10-10

最新評論

金川县| 岢岚县| 抚州市| 陆良县| 德庆县| 克东县| 靖州| 双牌县| 随州市| 齐齐哈尔市| 湘潭县| 麻江县| 伊宁市| 华宁县| 丽江市| 兴城市| 布尔津县| 祁门县| 蓬莱市| 双牌县| 巴彦淖尔市| 分宜县| 新闻| 岗巴县| 温宿县| 治多县| 岚皋县| 永顺县| 泗阳县| 金寨县| 延津县| 桂平市| 佳木斯市| 奈曼旗| 来安县| 阳东县| 九江县| 铜鼓县| 醴陵市| 响水县| 苏州市|