原文:Decorators and Functional Pythonhtml
譯者:youngsterxyfpython
裝飾器是Python的一大特點。除了在語言中的本來用處,還幫助咱們以一種有趣的方式(函數式)進行思考。git
我打算自底向上解釋裝飾器如何工做。首先解釋幾個話題以幫助理解裝飾器。而後,深刻一點探索幾個簡單的裝飾器以及它們如何工做。最後,討論一些更高級的使用裝飾器的方式,好比:傳遞可選參數給裝飾器或者串接幾個裝飾器。github
首先以我能想到的最簡單的方式來定義Python函數是什麼。基於該定義,咱們能夠相似的簡單方式來定義裝飾器。編程
函數是一個完成特定任務的可複用代碼塊。設計模式
好的,那麼裝飾器又是什麼呢?緩存
裝飾器是一個修改其餘函數的函數。數據結構
如今在裝飾器的定義上進行詳述,先解釋一些先決條件。閉包
Python中,全部東西都是對象。這意味着能夠經過名字引用函數,以及像其餘對象那樣傳遞。例如:app
def traveling_function(): print "Here I am!" function_dict = { "func": traveling_function } trav_func = function_dict['func'] trav_func() # >> Here I am!
traveling_function
被賦值給 function_dict
字典中鍵 func
的值,仍舊能夠正常調用。
咱們能夠像其餘對象那樣傳遞函數。能夠將函數做爲值傳遞給字典,放在列表中,或者做爲對象的屬性進行賦值。那爲何不能做爲參數傳給另外一個函數呢?固然能夠!若是一個函數接受另外一個函數做爲其參數或者返回另外一個函數,則稱之爲高階函數。
def self_absorbed_function(): return "I'm an amazing function!" def printer(func): print "The function passed to me says: " + func() # Call `printer` and give it `self_absorbed_function` as an argument printer(self_absorbed_function) # >>> The function passed to me says: I'm an amazing function!
如今你也看到函數能夠做爲參數傳給另外一個函數,並且傳給函數的函數還能夠調用。這容許咱們建立一些有意思的函數,例如裝飾器。
本質上,裝飾器就是一個以另外一個函數爲參數的函數。大多數狀況下,它們會返回所包裝函數的一個修改版本。來看個咱們能想到的最簡單的裝飾器—同一性(identity)裝飾器,或許對咱們理解裝飾器的工做原理有所幫助。
def identity_decorator(func): def wrapper(): func() return wrapper def a_function(): print "I'm a normal function." # `decorated_function` 是 `identity_function` 返回的函數,也就是嵌套函數 `wrapper` decorated_function = identity_function(a_function) # 以下調用 `identity_function` 返回的函數 decorated_function() # >>> I'm a normal function
這裏, identity_decorator
根本沒有修改它包裝的函數,只是簡單地返回一個函數(wrapper),這個函數在被調用之時,會去調用原來做爲 identity_decorator
參數的函數。這是個沒有用處的裝飾器!
關於 identity_decorator
的有趣之處是 wrapper
可以訪問變量 func
,即便 func
並不是是它的參數。這歸因於閉包。
閉包是一個花哨的術語,意爲聲明一個函數時,該函數會維持一個指向聲明所處詞法環境的引用。
上例中定義的函數 wrapper
可以在其局部做用域(local scope)中訪問 func
。這意味着在 wrapper
(返回並賦值給變量 decorated_function
)的整個生命週期內,它均可以訪問 func
變量。一旦 identity_decorator
返回,那麼訪問 func
的惟一方式就是經過 decorated_function
。 func
只做爲一個變量存在於 decorated_function
做用域環境的內部。
如今咱們來建立一個確實有點用的裝飾器。這個裝飾器所作的就是記錄它所修改的函數被調用了多少次。
def logging_decorator(func): def wrapper(): wrapper.count += 1 print "The function I modify has been called {0} time(s)".format(wrapper.count) func() wrapper.count = 0
return wrapper def a_function(): print "I'm a normal function." modified_function = logging_decorator(a_function) modified_function() # >>> The function I modify has been called 1 time(s). # >>> I'm a normal function. modified_function() # >>> The function I modify has been called 2 time(s). # >>> I'm a normal function.
咱們說裝飾器會修改函數,這樣來想對理解也是有幫助的。但如例子所見, logging_decorator
返回的是一個相似於a_function
的新函數,只是多了一個日誌特性。
上例中, logging_decorator
不只接受一個函數做爲參數,而且返回一個函數, wrapper
。每次logging_decorator
返回的函數獲得調用,它就對 wrapper.count
的值加1,打印出來,而後調用logging_decorator
包裝的函數。
你也許正疑惑爲何咱們的計數器是 wrapper
的一個屬性而不是一個普通的變量。難道 wrapper
的閉包環境不是讓咱們訪問在其局部做用域中聲明的任意變量麼?是的,但有個問題。Python中,閉包容許對其函數做用域鏈中任一變量的進行任意讀操做,但只容許對可變對象(列表、字典、等等)進行寫操做。整數在Python中是非可變對象,所以咱們不能修改 wrapper
內部整型變量的值。相反,咱們將計數器做爲 wrapper
的一個屬性—一個可變對象,所以能夠隨咱們本身增大它的值。
若是改爲普通變量,會報錯:UnboundLocalError: local variable 'count' referenced before assignment。這裏能夠參考:
http://www.cnblogs.com/youxin/p/3730610.html
在前一個例子中,咱們看到能夠將一個函數做爲參數傳給裝飾器,從而使用裝飾器函數對該函數進行包裝。然而,Python還有一個語法模式使得這一切更加直觀,更容易閱讀,一旦你熟悉了裝飾器。
# In the previous example, we used our decorator function by passing the # function we wanted to modify to it, and assigning the result to a variable def some_function(): print "I'm happiest when decorated." # Here we will make the assigned variable the same name as the wrapped function some_function = logging_decorator(some_function)
# We can achieve the exact same thing with this syntax: @logging_decorator def some_function(): print "I'm happiest when decorated"
使用裝飾器語法,鳥瞰其中發生的事情:
some_function
,並將其命名爲 ‘some_function’。logging_function
)。some_function
),綁定到變量名some_function
。將這些步驟記住,讓咱們來更清晰地解釋 identity_decorator
。
def identity_decorator(func): # Everything here happens when the decorator LOADS and is passed # the function as decribed in step 2 above def wrapper(): # Things here happen each time the final wrapped function gets CALLED func() return wrapper
但願那些註釋有助於理解。每次調用被包裝的函數,僅執行裝飾器返回的函數中的指令。返回函數以外的指令僅執行一次—上述步驟2中描述的:裝飾器首次接收到傳遞給它的待包裝函數之時。
在觀察更多的有意思的裝飾器以前,我想再解釋同樣東西。
之前你也許有時會把這二者相混淆了。讓咱們一次性地討論它們。
*args
語法,python函數可以接收可變數量的位置參數(positional arguments)。 *args
會將全部沒有關鍵字的參數放入一個參數元組中,在函數裏能夠訪問元組中的參數。相反,將 *args
用於函數調用時的實參列表之時,它會將參數元組展開成一系列的位置參數。def function_with_many_arguments(*args): print args # `args` within the function will be a tuple of any arguments we pass # which can be used within the function like any other tuple function_with_many_arguments('hello', 123, True) # >>> ('hello', 123, True)
def function_with_3_parameters(num, boolean, string):
print "num is " + str(num)
print "boolean is " + str(boolean)
print "string is " + string
arg_list = [1, False, 'decorators']
# arg_list will be expanded into 3 positional arguments by the `*` symbol function_with_3_parameters(*arg_list)
# >>> num is 1
# >>> boolean is False
# >>> string is decorators
重述一遍:在形參列表中, *args
會將一系列的參數壓縮進一個名爲’args’的元組,而在實參列表中, *args
會將一個可迭代的參數數據結構展開爲一系列的位置實參應用於函數。
如你所見在實參展開的例子中, *
符號可與’args’以外的名字一塊兒使用。當壓縮/展開通常的參數列表,使用 *args
的形式僅僅是一種慣例。
**kwargs
與 *args
的行爲相似,但用於關鍵字參數而非位置參數。若是在函數的形參列表中使用 **kwargs
,它會收集函數收到的全部額外關鍵字參數,放入一個字典中。若是用於函數的實參列表,它會將一個字典展開爲一系列的關鍵字參數。def funtion_with_many_keyword_args(**kwargs): print kwargs function_with_many_keyword_args(a='apples', b='bananas', c='cantalopes') # >> {'a':'apples', 'b':'bananas', 'c':'cantalopes'}
def multiply_name(count=0, name=''): print name * count arg_dict = {'count': 3, 'name': 'Brian'} multiply_name(**arg_dict) # >> BrianBrianBrian
既然你理解了 *args
與 **kwargs
的工做原理,那麼咱們就繼續研究一個你會發現頗有用的裝飾器。
緩存製表是避免潛在的昂貴的重複計算的一種方法,經過緩存函數每次執行的結果來實現。這樣,下一次函數以相同的參數執行,就能夠從緩存中獲取返回結果,不須要再次計算結果。
from functools import wraps def memoize(func): cache = {} @wraps(func) def wrapper(*args): if args not in cache: cache[args] = func(*args) return cache[args] return wrapper @memoize def an_expensive_function(arg1, arg2, arg3): ...
你可能注意到了示例代碼中一個奇怪的 @wraps
裝飾器。在完整地討論 memoize
以前我將簡要地解釋這個裝飾器。
__name__
, __doc__
, 以及 __module__
屬性。 wraps
函數是一個包裝另外一個裝飾器返回的函數的裝飾器,將那三個屬性的值恢復爲函數未裝飾之時的值。例如: 若是不使用 wraps
裝飾器, an_expensive_function
的名字(經過 an_expensive_function.__name__
能夠看到)將是 ‘wrapper’ 。我認爲 memoize
是一個很好的裝飾器用例。它服務於一個不少函數都須要的目的,經過將它建立爲一個通用裝飾器,咱們能夠將它的功能應用於任一可以從其中獲益的函數。這就避免了在多種不一樣的場合重複實現這個功能。由於不須要重複本身,因此咱們的代碼更容易維護,而且更容易閱讀和理解。只要讀一個單詞你就能馬上理解函數使用了緩存製表。
須要提醒的是:緩存製表僅適用於純函數。也就是說給定一個特定的參數設置,函數肯定總會產生相同的結果。若是函數依賴於不做爲參數傳遞的全局變量、I/O、或者其它任意可能影響返回值的東西,緩存製表會產生使人迷惑的結果!而且,一個純函數不會有任何反作用。所以,若是你的函數會增大一個計數器,或者調用另外一個對象的方法,或者其它任意不在函數的返回結果中表示的東西,當結果是從緩存中返回時,反作用操做並不會獲得執行。
最初,咱們說裝飾器是一個修改另外一個函數的函數,但其實它們能夠用於修改類或者方法。對類進行裝飾並不常見,但某些狀況下做爲元類(metaclass)的一個替代,類的裝飾器是一個有用的工具。
foo = ['important', 'foo', 'stuff'] def add_foo(klass): klass.foo = foo return klass @add_foo class Person(object): pass brian = Person() print brian.foo # >> ['important', 'foo', 'stuff']
如今,類 Person
的任一對象都有一個超級重要的 foo
屬性!注意,由於咱們裝飾的是一個類,因此裝飾器返回的不是一個函數,而是一個類。更新一下裝飾器的定義:
裝飾器是一個修改函數、或方法、或類的函數。
事實證實我早先對你隱瞞了一些其它事情。不只裝飾器能夠裝飾一個類,而且裝飾器也能夠是一個類!對於裝飾器的惟一要求就是它的返回值必須可調用(callable)。這意味着裝飾器必須實現 __call__
魔術方法,當你調用一個對象時,會隱式調用這個方法。函數固然是隱式設置這個方法的。咱們從新將 identity_decorator
建立爲一個類來看看它是如何工做的。
class IdentityDecorator(object): def __init__(self, func): self.func = func def __call__(self): self.func() @IdentityDecorator def a_function(): print "I'm a normal function." a_function() # >> I'm a normal function.
以下是上例中發生的事情:
當 IdentityDecorator
裝飾 a_function
時,它的行爲就和裝飾器函數同樣。這個代碼片斷等價於上例中的裝飾器語法: a_function = IdentityDecorator(a_function)
。調用(實例化)該裝飾器類時,需將其裝飾的函數做爲一個實參傳遞給它。
實例化 IdentityDecorator
之時,會以被裝飾的函數做爲實參調用初始化函數 __init__
。本例中,初始化函數所作的事情就是將被裝飾函數賦值給一個屬性,這樣以後就能夠經過其它方法進行調用。
最後,調用 a_function
(其實是返回的包裝了 a_function
的 IdentityDecorator
對象)之時,會調用對象的 __call__
方法。這僅是一個同一性裝飾器,因此方法只是簡單地調用了該類所裝飾的函數。
再次更新一下咱們對裝飾器的定義!
裝飾器是一個修改函數、方法或者類的可調用對象。
有時,須要根據不一樣的狀況改變裝飾器的行爲。你能夠經過傳參來作到這一點。
from functools import wraps def argumentative_decorator(gift): def func_wrapper(func): @wraps(func) def returned_wrapper(*arg, **kwargs): print "I don't like this " + gift + "you gave me!" return func(gift, *args, **kwargs) return returned_wrapper return func_wrapper @argumentative_decorator("sweater") def grateful_function(gift): print "I love the " + gift + "!Thank you!" grateful_function() # >> I don't like this sweater you gave me! # >> I love the sweater! Thank you!
咱們來看看若是不使用裝飾器語法這個裝飾器函數是如何工做的:
# If we tried to invoke without an argument: grateful_function = argumentative_function(grateful_function) # But when given an argument, the pattern changes to: grateful_function = argumentative_decorator("sweater")(grateful_function)
須要注意的地方是:當給定參數,首先僅以那些參數調用裝飾器—被包裝的函數並不在參數中。裝飾器調用返回後,裝飾器要包裝的函數被傳遞給裝飾器初始調用返回的函數(本例中,爲 argumentative_decorator("sweater")
的返回值)。
逐步地:
grateful_function
,並將其綁定到名字’grateful_function’。argumentative_decorator
,返回 func_wrapper
。grateful_function
爲參調用 func_wrapper
,返回 returned_wrapper
。returned_wrapper
取代原來的函數 grateful_function
,並綁定到名字’grateful_function’ 。我想這一過程相比沒有裝飾器參數理解起來更難一點,可是若是你花些時間將其理解通透,我但願是有意義的。
有多種方式讓裝飾器接受可選參數。根據你是想使用位置參數、關鍵字參數仍是二者皆是,須要使用稍微不一樣的模式。以下我將展現一種接受一個可選關鍵字參數的方式:
from functools import wraps GLOBAL_NAME = "Brian" def print_name(function=None, name=GLOBAL_NAME): def actual_decorator(function): @wraps(function) def returned_func(*args, **kwargs): print "My name is " + name return function(*args, **kwargs) return returned_func if not function: # User passed in a name argument def waiting_for_func(function): return actual_decorator(function) return waiting_for_func else: return actual_decorator(function) @print_name def a_function(): print "I like the name!" @print_name(name='Matt') def another_function(): print "Hey, that's new!" a_function() # >> My name is Brian # >> I like that name! another_function() # >> My name is Matt # >> Hey, that's new!
若是咱們傳遞關鍵字參數 name
給 print_name
,那麼它的行爲就與前一個例子中的 argumentative_decorator
類似。即,首先以 name
爲參調用 print_name
。而後,將待包裝的函數傳遞給首次調用返回的函數。
若是咱們沒有提供 name
實參, print_name
的行爲就與前面咱們看到的不帶參數的裝飾器同樣。裝飾器僅以待包裝的函數做爲惟一的參數進行調用。
print_name
支持兩種可能性。它會檢查是否收到做爲參數的被包裝函數。若是沒有,則返回函數 waiting_for_func
,該函數能夠被包裝函數做爲參數進行調用。若是收到被包裝函數做爲參數,則跳過中間步驟,直接調用actual_decorator
。
如今來探索一下今天要講的最後一個裝飾器的特性:串接。你能夠在任意給定的函數之上堆疊使用多個裝飾器, 這種構建函數的方式與使用多重繼承構建類相相似。不過最好不要瘋狂使用這種特性。
@print_name('Sam') @logging_decorator def some_function(): print "I'm the wrapped function!" some_function() # >> My name is Sam # >> The function I modify has been called 1 time(s). # >> I'm the wrapped function!
當你串接使用裝飾器時,它們堆疊的順序是自底向上的。將被包裝的函數 some_function
經編譯後傳遞給它之上的第一個裝飾器( logging_decorator
)。而後第一個裝飾器的返回值被傳遞給第二個裝飾器。依此逐個應用鏈上每一個裝飾器。
由於咱們使用的兩個裝飾器都是 print
一個值,而後執行傳遞給它們的函數,這意味着當調用被包裝函數時,鏈中的最後一個裝飾器 print_name
打印輸出中的第一行。
我認爲裝飾器最大的好處之一在於讓你可以從更高的抽象層次進行思考。假如你開始閱讀一個函數定義,看到有一個memoize
裝飾器,你馬上就能明白你正在看的是一個使用緩存製表的函數。若是緩存製表的代碼包含在函數體內,就會須要額外的腦力進行解析,而且會有引入誤解的可能。使用裝飾器也容許代碼複用,從而節省時間、簡化調試,而且使得重構更加容易。
玩玩裝飾器也是一種很好的學習函數式概念(如高階函數與閉包)的方式。
我但願本文閱讀起來很愉快,而且內容翔實。
轉自:http://youngsterxyf.github.io/2013/01/04/Decorators-and-Functional-Python/
python裝飾器解釋:
今天來討論一下裝飾器。裝飾器是一個很著名的設計模式,常常被用於有切面需求的場景,較爲經典的有插入日誌、性能測試、事務處理等。裝飾器是解決這類問題的絕佳設計,有了裝飾器,咱們就能夠抽離出大量函數中與函數功能自己無關的雷同代碼並繼續重用。歸納的講,裝飾器的做用就是爲已經存在的對象添加額外的功能。
裝飾器的定義非常抽象,咱們來看一個小例子。
1
2
3
4
|
def
foo():
print
'in foo()'
foo()
|
這是一個很無聊的函數沒錯。可是忽然有一個更無聊的人,咱們稱呼他爲B君,說我想看看執行這個函數用了多長時間,好吧,那麼咱們能夠這樣作:
1
2
3
4
5
6
7
8
|
import
time
def
foo():
start
=
time.clock()
print
'in foo()'
end
=
time.clock()
print
'used:'
, end
-
start
foo()
|
很好,功能看起來無懈可擊。但是蛋疼的B君此刻忽然不想看這個函數了,他對另外一個叫foo2的函數產生了更濃厚的興趣。
怎麼辦呢?若是把以上新增長的代碼複製到foo2裏,這就犯了大忌了~複製什麼的難道不是最討厭了麼!並且,若是B君繼續看了其餘的函數呢?
還記得嗎,函數在Python中是一等公民,那麼咱們能夠考慮從新定義一個函數timeit,將foo的引用傳遞給他,而後在timeit中調用foo並進行計時,這樣,咱們就達到了不改動foo定義的目的,並且,不論B君看了多少個函數,咱們都不用去修改函數定義了!
1
2
3
4
5
6
7
8
9
10
11
12
|
import
time
def
foo():
print
'in foo()'
def
timeit(func):
start
=
time.clock()
func()
end
=
time.clock()
print
'used:'
, end
-
start
timeit(foo)
|
看起來邏輯上並無問題,一切都很美好而且運做正常!……等等,咱們彷佛修改了調用部分的代碼。本來咱們是這樣調用的:foo(),修改之後變成了:timeit(foo)。這樣的話,若是foo在N處都被調用了,你就不得不去修改這N處的代碼。或者更極端的,考慮其中某處調用的代碼沒法修改這個狀況,好比:這個函數是你交給別人使用的。
既然如此,咱們就來想一想辦法不修改調用的代碼;若是不修改調用代碼,也就意味着調用foo()須要產生調用timeit(foo)的效果。咱們能夠想到將timeit賦值給foo,可是timeit彷佛帶有一個參數……想辦法把參數統一吧!若是timeit(foo)不是直接產生調用效果,而是返回一個與foo參數列表一致的函數的話……就很好辦了,將timeit(foo)的返回值賦值給foo,而後,調用foo()的代碼徹底不用修改!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
#-*- coding: UTF-8 -*-
import
time
def
foo():
print
'in foo()'
# 定義一個計時器,傳入一個,並返回另外一個附加了計時功能的方法
def
timeit(func):
# 定義一個內嵌的包裝函數,給傳入的函數加上計時功能的包裝
def
wrapper():
start
=
time.clock()
func()
end
=
time.clock()
print
'used:'
, end
-
start
# 將包裝後的函數返回
return
wrapper
foo
=
timeit(foo)
foo()
|
這樣,一個簡易的計時器就作好了!咱們只須要在定義foo之後調用foo以前,加上foo = timeit(foo),就能夠達到計時的目的,這也就是裝飾器的概念,看起來像是foo被timeit裝飾了。在在這個例子中,函數進入和退出時須要計時,這被稱爲一個橫切面(Aspect),這種編程方式被稱爲面向切面的編程(Aspect-Oriented Programming)。與傳統編程習慣的從上往下執行方式相比較而言,像是在函數執行的流程中橫向地插入了一段邏輯。在特定的業務領域裏,能減小大量重複代碼。面向切面編程還有至關多的術語,這裏就很少作介紹,感興趣的話能夠去找找相關的資料。
這個例子僅用於演示,並無考慮foo帶有參數和有返回值的狀況,完善它的重任就交給你了 :)
上面這段代碼看起來彷佛已經不能再精簡了,Python因而提供了一個語法糖來下降字符輸入量。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
import
time
def
timeit(func):
def
wrapper():
start
=
time.clock()
func()
end
=
time.clock()
print
'used:'
, end
-
start
return
wrapper
@timeit
def
foo():
print
'in foo()'
foo()
|
重點關注第11行的@timeit,在定義上加上這一行與另外寫foo = timeit(foo)徹底等價,千萬不要覺得@有另外的魔力。除了字符輸入少了一些,還有一個額外的好處:這樣看上去更有裝飾器的感受。
內置的裝飾器有三個,分別是staticmethod、classmethod和property,做用分別是把類中定義的實例方法變成靜態方法、類方法和類屬性。因爲模塊裏能夠定義函數,因此靜態方法和類方法的用處並非太多,除非你想要徹底的面向對象編程。而屬性也不是不可或缺的,Java沒有屬性也同樣活得很滋潤。從我我的的Python經驗來看,我沒有使用過property,使用staticmethod和classmethod的頻率也很是低。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
class
Rabbit(
object
):
def
__init__(
self
, name):
self
._name
=
name
@staticmethod
def
newRabbit(name):
return
Rabbit(name)
@classmethod
def
newRabbit2(
cls
):
return
Rabbit('')
@property
def
name(
self
):
return
self
._name
|
這裏定義的屬性是一個只讀屬性,若是須要可寫,則須要再定義一個setter:
1
2
3
|
@name
.setter
def
name(
self
, name):
self
._name
=
name
|
functools模塊提供了兩個裝飾器。這個模塊是Python 2.5後新增的,通常來講你們用的應該都高於這個版本。但我平時的工做環境是2.4 T-T
2.3.1. wraps(wrapped[, assigned][, updated]):
這是一個頗有用的裝飾器。看過前一篇反射的朋友應該知道,函數是有幾個特殊屬性好比函數名,在被裝飾後,上例中的函數名foo會變成包裝函數的名字wrapper,若是你但願使用反射,可能會致使意外的結果。這個裝飾器能夠解決這個問題,它能將裝飾過的函數的特殊屬性保留。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
import
time
import
functools
def
timeit(func):
@functools
.wraps(func)
def
wrapper():
start
=
time.clock()
func()
end
=
time.clock()
print
'used:'
, end
-
start
return
wrapper
@timeit
def
foo():
print
'in foo()'
foo()
print
foo.__name__
|
首先注意第5行,若是註釋這一行,foo.__name__將是'wrapper'。另外相信你也注意到了,這個裝飾器居然帶有一個參數。實際上,他還有另外兩個可選的參數,assigned中的屬性名將使用賦值的方式替換,而updated中的屬性名將使用update的方式合併,你能夠經過查看functools的源代碼得到它們的默認值。對於這個裝飾器,至關於wrapper = functools.wraps(func)(wrapper)。
2.3.2. total_ordering(cls):
這個裝飾器在特定的場合有必定用處,可是它是在Python 2.7後新增的。它的做用是爲實現了至少__lt__、__le__、__gt__、__ge__其中一個的類加上其餘的比較方法,這是一個類裝飾器。若是以爲很差理解,不妨仔細看看這個裝飾器的源代碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
53
def
total_ordering(
cls
):
54
"""Class decorator that fills in missing ordering methods"""
55
convert
=
{
56
'__lt__'
: [(
'__gt__'
,
lambda
self
, other: other <
self
),
57
(
'__le__'
,
lambda
self
, other:
not
other <
self
),
58
(
'__ge__'
,
lambda
self
, other:
not
self
< other)],
59
'__le__'
: [(
'__ge__'
,
lambda
self
, other: other <
=
self
),
60
(
'__lt__'
,
lambda
self
, other:
not
other <
=
self
),
61
(
'__gt__'
,
lambda
self
, other:
not
self
<
=
other)],
62
'__gt__'
: [(
'__lt__'
,
lambda
self
, other: other >
self
),
63
(
'__ge__'
,
lambda
self
, other:
not
other >
self
),
64
(
'__le__'
,
lambda
self
, other:
not
self
> other)],
65
'__ge__'
: [(
'__le__'
,
lambda
self
, other: other >
=
self
),
66
(
'__gt__'
,
lambda
self
, other:
not
other >
=
self
),
67
(
'__lt__'
,
lambda
self
, other:
not
self
>
=
other)]
68
}
69
roots
=
set
(
dir
(
cls
)) &
set
(convert)
70
if
not
roots:
71
raise
ValueError(
'must define at least one ordering operation: < > <= >='
)
72
root
=
max
(roots)
# prefer __lt__ to __le__ to __gt__ to __ge__
73
for
opname, opfunc
in
convert[root]:
74
if
opname
not
in
roots:
75
opfunc.__name__
=
opname
76
opfunc.__doc__
=
getattr
(
int
, opname).__doc__
77
setattr
(
cls
, opname, opfunc)
78
return
cls
|
本文到這裏就所有結束了,有空的話我會整理一個用於檢查參數類型的裝飾器的源代碼放上來,算是一個應用吧 :
轉自:http://www.cnblogs.com/huxi/archive/2011/03/01/1967600.html#2817508)
更多:http://www.codecho.com/understanding-python-decorators/
http://doc.42qu.com/python/python-closures-and-decorators.html
http://www.cnblogs.com/rhcad/archive/2011/12/21/2295507.html