python函数修饰器_Python函数装饰器指南
Python 具有強(qiáng)大的功能和富有表現(xiàn)力的語(yǔ)法。我最喜歡的裝飾之一。在設(shè)計(jì)模式的上下文中,裝飾器動(dòng)態(tài)更改方法或類功能,而不必直接使用子類。當(dāng)您需要擴(kuò)展功能,但不想修改原函數(shù)時(shí),這是理想的選擇。我們可以在任何地方實(shí)現(xiàn)裝飾器模式,但是 Python 通過(guò)提供更具表現(xiàn)力的功能和語(yǔ)法來(lái)促進(jìn)實(shí)現(xiàn)。
在這篇文章中,將討論 Python 的函數(shù)裝飾器,并附帶一些澄清有關(guān)概念的示例。所有示例均適用 Python 2.7,但相同的概念應(yīng)適用于Python 3,但語(yǔ)法有所更改。
本質(zhì)上,裝飾器充當(dāng)包裝器,在目標(biāo)函數(shù)執(zhí)行之前和之后修改代碼的行為,而無(wú)需修改函數(shù)本身,從而增強(qiáng)了原始功能,從而對(duì)其進(jìn)行了裝飾。
您需要了解的功能
在潛水之前,應(yīng)先弄清一些先決條件。在 Python 中,函數(shù)是一等公民,它們是對(duì)象,這意味著我們可以用它們做很多有用的事情。
將函數(shù)分配給變量
1
2
3
4
5
6
7
def greet(name):
return "hello "+name
greet_someone = greet
print(greet_someone("John"))
# 輸出: hello John
在其他函數(shù)中定義函數(shù)
1
2
3
4
5
6
7
8
9
10
def greet(name):
def get_message():
return "Hello "
result = get_message()+name
return result
print(greet("John"))
# 輸出: Hello John
可以將函數(shù)作為參數(shù)傳遞給其他函數(shù)
1
2
3
4
5
6
7
8
9
10
def greet(name):
return "Hello " + name
def call_func(func):
other_name = "John"
return func(other_name)
print(call_func(greet))
# 輸出: Hello John
函數(shù)可以返回其他函數(shù)
換句話說(shuō), 函數(shù)生成其他函數(shù)。
1
2
3
4
5
6
7
8
9
10
def compose_greet_func():
def get_message():
return "Hello there!"
return get_message
greet = compose_greet_func()
print(greet())
# 輸出: Hello there!
內(nèi)部函數(shù)可以訪問(wèn)封閉范圍
更通常稱為閉包。在構(gòu)建裝飾器時(shí)會(huì)遇到的一種非常強(qiáng)大的模式。還要注意的另一件事是,Python 只允許對(duì)外部作用域進(jìn)行讀取訪問(wèn),而不是賦值。請(qǐng)注意,我們?nèi)绾涡薷纳厦娴氖纠詮膬?nèi)部函數(shù)的封閉范圍中讀取“name” 參數(shù)并返回新函數(shù)。
1
2
3
4
5
6
7
8
9
10
def compose_greet_func(name):
def get_message():
return "Hello there "+name+"!"
return get_message
greet = compose_greet_func("John")
print(greet())
# 輸出: Hello there John!
裝飾者的組成
函數(shù)裝飾器只是現(xiàn)有函數(shù)的包裝器。綜上所述,我們可以構(gòu)建一個(gè)裝飾器。在此示例中,我們考慮一個(gè)函數(shù),該函數(shù)通過(guò)p標(biāo)簽包裝另一個(gè)函數(shù)的字符串輸出。
1
2
3
4
5
6
7
8
9
10
11
12
13
def get_text(name):
return "lorem ipsum, {0} dolor sit amet".format(name)
def p_decorate(func):
def func_wrapper(name):
return "
{0}
".format(func(name))return func_wrapper
my_get_text = p_decorate(get_text)
print(my_get_text("John"))
# 輸出:
lorem ipsum, John dolor sit amet
那是我們的第一個(gè)裝飾。一個(gè)將另一個(gè)函數(shù)作為參數(shù)的函數(shù),將生成一個(gè)新函數(shù),以擴(kuò)展原始函數(shù)的功能,并返回生成的函數(shù),以便我們可以在任何地方使用它。要讓 get_text 本身由 p_decorate 裝飾,我們只需將 p_decorate 的結(jié)果再賦值給 get_text 即可。
1
2
3
4
5
get_text = p_decorate(get_text)
print(get_text("John"))
# 輸出:
lorem ipsum, John dolor sit amet
還要注意的另一點(diǎn)是,我們的修飾函數(shù)帶有一個(gè) name 參數(shù)。在裝飾器中我們要做的就是讓 get_text 的包裝傳遞該參數(shù)。
Python的裝飾語(yǔ)法
Python通過(guò)一些語(yǔ)法糖使創(chuàng)建和使用裝飾器對(duì)程序員來(lái)說(shuō)更干凈,更友好。不必裝飾 get_text,get_text = p_decorator(get_text) 它有一個(gè)捷徑,即在要使用的函數(shù)之前提供裝飾函數(shù)的名稱即可。裝飾器的名稱應(yīng)帶有@符號(hào)。
1
2
3
4
5
6
7
8
9
10
11
12
def p_decorate(func):
def func_wrapper(name):
return "
{0}
".format(func(name))return func_wrapper
@p_decorate
def get_text(name):
return "lorem ipsum, {0} dolor sit amet".format(name)
print(get_text("John"))
# 輸出:
lorem ipsum, John dolor sit amet
現(xiàn)在,讓我們考慮我們要用其他2個(gè)函數(shù)來(lái)修飾 get_text 函數(shù),以便在字符串輸出周圍包裝div和strong標(biāo)簽。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
def p_decorate(func):
def func_wrapper(name):
return "
{0}
".format(func(name))return func_wrapper
def strong_decorate(func):
def func_wrapper(name):
return "{0}".format(func(name))
return func_wrapper
def div_decorate(func):
def func_wrapper(name):
return "
{0} ".format(func(name))return func_wrapper
使用基本方法,裝飾 get_text 將遵循以下步驟:
1
get_text = div_decorate(p_decorate(strong_decorate(get_text)))
使用 Python 的裝飾器語(yǔ)法,可以用更具表達(dá)力的功能實(shí)現(xiàn)相同功能。
1
2
3
4
5
6
7
8
9
@div_decorate
@p_decorate
@strong_decorate
def get_text(name):
return "lorem ipsum, {0} dolor sit amet".format(name)
print(get_text("John"))
# 輸出:
lorem ipsum, John dolor sit amet
這里要注意的一件事是設(shè)置裝飾器的順序很重要。如果以上示例中的順序不同,則輸出將不同。
裝飾方式
在 Python 中,方法是期望其第一個(gè)參數(shù)成為對(duì)當(dāng)前對(duì)象的引用的函數(shù)。我們可以以相同的方式為方法構(gòu)建裝飾器,同時(shí)在包裝函數(shù)中考慮自身。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def p_decorate(func):
def func_wrapper(self):
return "
{0}
".format(func(self))return func_wrapper
class Person(object):
def __init__(self):
self.name = "John"
self.family = "Doe"
@p_decorate
def get_fullname(self):
return self.name+" "+self.family
my_person = Person()
print(my_person.get_fullname())
更好的方法是使裝飾器對(duì)函數(shù)和方法都有用。這可以通過(guò)將*args 和 **kwargs作為包裝器的參數(shù)來(lái)完成,然后它可以接受任意數(shù)量的參數(shù)和關(guān)鍵字參數(shù)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def p_decorate(func):
def func_wrapper(*args, **kwargs):
return "
{0}
".format(func(*args, **kwargs))return func_wrapper
class Person(object):
def __init__(self):
self.name = "John"
self.family = "Doe"
@p_decorate
def get_fullname(self):
return self.name+" "+self.family
my_person = Person()
print(my_person.get_fullname())
將參數(shù)傳遞給裝飾器
回顧上面的示例之前的示例,您會(huì)注意到示例中的裝飾器是多么冗余。3個(gè)裝飾器(div_decorate,p_decorate,strong_decorate)具有相同的功能,但用不同的標(biāo)簽包裝字符串。我們絕對(duì)可以做得更好。為什么不為將標(biāo)簽包裝為字符串的標(biāo)簽提供更通用的實(shí)現(xiàn)?是的,請(qǐng)!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
def tags(tag_name):
def tags_decorator(func):
def func_wrapper(name):
return "<{0}>{1}{0}>".format(tag_name, func(name))
return func_wrapper
return tags_decorator
@tags("p")
def get_text(name):
return "Hello "+name
print(get_text("John"))
# 輸出
Hello John
在這種情況下,需要做更多的工作。裝飾器期望接收一個(gè)函數(shù)作為參數(shù),這就是為什么我們必須構(gòu)建一個(gè)接受這些額外參數(shù)并動(dòng)態(tài)生成裝飾器的原因。在上面的示例tags,是我們的裝飾器生成器。
調(diào)試裝飾功能
歸根結(jié)底,裝飾器只是包裝我們的函數(shù),以防調(diào)試出現(xiàn)問(wèn)題,因?yàn)榘b器函數(shù)不攜帶原始函數(shù)的名稱,模塊和文檔字符串。基于上面的示例,如果我們這樣做:
1
2
print(get_text.__name__)
# 輸出 func_wrapper
期待輸出get_text,然而,get_text的__name__,__doc__和__module__屬性被包裝(func_wrapper)覆蓋。顯然,我們可以在func_wrapper中重置它們,但是Python提供了一種更好的方法。
救援工具
幸運(yùn)的是,Python(從版本2.5開始)包括functools模塊,其中包含functools.wraps。Wraps 是一個(gè)修飾器,用于將包裝函數(shù)(func_wrapper)的屬性更新為原始函數(shù)(get_text)的屬性。這就像通過(guò)@wraps(func)裝飾func_wrapper一樣簡(jiǎn)單。這是更新的示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from functools import wraps
def tags(tag_name):
def tags_decorator(func):
@wraps(func)
def func_wrapper(name):
return "<{0}>{1}{0}>".format(tag_name, func(name))
return func_wrapper
return tags_decorator
@tags("p")
def get_text(name):
"""returns some text"""
return "Hello "+name
print(get_text.__name__) # get_text
print(get_text.__doc__) # returns some text
print(get_text.__module__) # __main__
您可以從輸出中注意到,get_text 的屬性現(xiàn)在是正確的屬性。
裝飾器在哪里使用
相對(duì)于您可以使用裝飾器完成的工作量,本文中的示例非常簡(jiǎn)單。它們可以為您的程序提供如此強(qiáng)大的功能。通常,裝飾器是擴(kuò)展我們不想修改的函數(shù)的行為的理想選擇。有關(guān)有用的裝飾器的大量清單,建議您查看Python Decorator Library
總結(jié)
以上是生活随笔為你收集整理的python函数修饰器_Python函数装饰器指南的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 使用qsort对不连续的内存数据排序_数
- 下一篇: python数据储存_五种使用pytho