關于Python Decorator你應該知道的一切

關鍵詞:Python、裝飾器、裝飾器的深入討論、Python decorator

前言

最近學習Python,在看一個框架源碼過程中對裝飾器很困惑,Google了一圈,在stack overflow的一個問題討論下面找到了這個總結,這里幾乎有關于Python全部的內容。覺得很好,遂翻譯過來。翻譯基本都是意譯,能看英文的還是盡量看上面鏈接過去的原版吧!

Pyton裝飾器基礎

在Python中,函數也是對象

為了理解裝飾器,你必須首先理解,在Python中函數也是對象。
理解這個知識點很重要。讓我們使用一個簡單的例子來說明一下:

def shout(word="yes"):
    return word.capitalize()+"!"

print shout()
# 輸出為: 'Yes!'


# 函數作為一個對象,你可以像其他對象一樣,把它賦值給其他的變量
scream = shout

# 注意我們沒有使用圓括號:我們不是調用這個函數,我們把"shout"這個函數賦值給變量"scream"   
# 那意味著你之后可以使用"scream"來調用"shout"這個函數
print scream()
# 輸出為: 'Yes!'

# 不僅如此,那意味著你可以移除'shout'這個老的名稱,但這個函數仍然可以通過'scream'訪問
del shout
try:
    print shout()
except NameError, e:
    print e
    # 輸出為: "name 'shout' is not defined"

print scream()
# 輸出為: 'Yes!'

好了,在心里記住這個知識點。我們之后很快要用到它。

Python中函數還有另一個有趣的特性,那就是它可以在其他函數里面定義!

def talk():

    # 你可以在"talk"函數中定義一個函數...
    def whisper(word="yes"):
        return word.lower()+"..."

     # ...并且你可以馬上使用這個函數
    print whisper()

# 你每次調用"talk"這個函數的時候,它會定義一個"whisper"函數,之后這個"whisper"將在"talk"里面被調用
talk()
# 輸出為:"yes..."

# 但是在"talk"這個函數的作用域之外,"whisper"這個函數是不存在的
try:
    print whisper()
except NameError, e:
    print e
    # 輸出為: "name 'whisper' is not defined"*

函數的引用

Okay,就這些東西嗎?有趣的部分該上場了...
你已經看見,函數是對象。因此,函數:

  • 可以賦值給其他變量
  • 可以在其它函數里面定義

那意味著一個函數可以被另一個函數return。我們來看個例子! ?

def getTalk(kind="shout"):

    # 我們定義了一些函數
    def shout(word="yes"):
        return word.capitalize()+"!"

    def whisper(word="yes") :
        return word.lower()+"...";

    # 然后我們返回他們中的一個
    if kind == "shout":
        # 我們沒有用"()", 我們不是要調用這個函數
        # 我們返回了這個函數對象
        return shout  
    else:
        return whisper

# 我們怎么使用它呢?
# 獲取函數,并將它賦值給一個變量
talk = getTalk()      

# 你可以看到在這里"talk"是一個函數對象:
print talk
# 輸出為: <function shout at 0xb7ea817c>

# 這個就是被函數返回的對象
print talk()
# 輸出為: Yes!

# 你甚至可以直接使用它:
print getTalk("whisper")()
# 輸出為: yes...

等等...這里有我們沒有注意到的地方!

既然你可以return一個函數,你就可以把一個函數當參數傳遞:

def doSomethingBefore(func): 
    print "I do something before then I call the function you gave me"
    print func()

doSomethingBefore(scream)
# 輸出為: 
#I do something before then I call the function you gave me
#Yes!

好了,你已經具備了理解裝飾器的所有知識點。你知道,裝飾器就是 "封裝", 這意味著它可以讓你在被它裝飾的函數前面和后面執行一些代碼,而不必改動被裝飾的函數本身。

手動創建裝飾器

你如何手動構建一個裝飾器:

# 裝飾是一個函數,該函數需要另一個函數作為它的參數
def my_shiny_new_decorator(a_function_to_decorate):

    # 在裝飾器的函數實現里面它定義了另一個函數: 他就是封裝函數(wrapper)
    # 這個函數將原來的函數封裝到里面
    # 因此你可以在原來函數的前面和后面執行一些附加代碼
    def the_wrapper_around_the_original_function():

        # 在這里放置你想在原來函數執行前執行的代碼
        print "Before the function runs"

        # 調用原來的函數(使用圓括號)
        a_function_to_decorate()

        # 在這里放置你想在原來函數執行后執行的代碼
        print "After the function runs"

    # 這個時候,"a_function_to_decorate"并沒有執行
    # 我們返回剛才創建的封裝函數
    # 這個封裝函數包含了原來的函數,和將在原來函數前面和后面執行的代碼。我們就可以使用它了!
    return the_wrapper_around_the_original_function

# 想象你創建了一個你再也不想修改的函數
def a_stand_alone_function():
    print "I am a stand alone function, don't you dare modify me"

a_stand_alone_function() 
# 輸出為: I am a stand alone function, don't you dare modify me

# 現在你可以裝飾這個函數來擴展它的行為
# 只需要將這個函數傳入裝飾器,那它將被動態的包在任何你想執行的代碼間,并且返回一個可被使用的新函數:
a_stand_alone_function_decorated = my_shiny_new_decorator(a_stand_alone_function)
a_stand_alone_function_decorated()
#輸出為:
#Before the function runs
#I am a stand alone function, don't you dare modify me
#After the function runs

現在,你可能想在每次調用 a_stand_alone_function的時候,真正被執行的函數是 a_stand_alone_function_decorated。那很容易,只需要使用 my_shiny_new_decorator返回的函數賦值給原來的 a_stand_alone_function這個函數名(其實是個變量):

a_stand_alone_function = my_shiny_new_decorator(a_stand_alone_function)
a_stand_alone_function()
#輸出為:
#Before the function runs
#I am a stand alone function, don't you dare modify me
#After the function runs

# 你猜怎么著?這就是裝飾器做的事情。

裝飾器揭秘

前面的例子,使用Python的裝飾器語法糖來重寫就是下面的樣子:

@my_shiny_new_decorator
def another_stand_alone_function():
    print "Leave me alone"

another_stand_alone_function()  
# 輸出為:  
#Before the function runs
#Leave me alone
#After the function runs

是的,這就是全部,就是這么簡單。@decorator 只是下面表達式的簡寫:

another_stand_alone_function = my_shiny_new_decorator(another_stand_alone_function)

這里的裝飾器只是裝飾器設計模式的一種Python化變體。Python嵌入了多種經典的設計模式來簡化開發(比如迭代器(iterators))。

當然,你可以堆積裝飾器(使用多層裝飾器):

def bread(func):
    def wrapper():
        print "</''''''\>"
        func()
        print "<\______/>"
    return wrapper

def ingredients(func):
    def wrapper():
        print "#tomatoes#"
        func()
        print "~salad~"
    return wrapper

def sandwich(food="--ham--"):
    print food

sandwich()
# 輸出為: --ham--
sandwich = bread(ingredients(sandwich))
sandwich()
#outputs:
#</''''''\>
# #tomatoes#
# --ham--
# ~salad~
#<\______/>

使用Python的裝飾器語法糖:

@bread
@ingredients
def sandwich(food="--ham--"):
    print food

sandwich()
#outputs:
#</''''''\>
# #tomatoes#
# --ham--
# ~salad~
#<\______/>

你放置裝飾器的順序很重要:

@ingredients
@bread
def strange_sandwich(food="--ham--"):
    print food

strange_sandwich()
#outputs:
##tomatoes#
#</''''''\>
# --ham--
#<\______/>
# ~salad~

現在: 回答問題(請參考stack overflorw上的相關問題)

作為結論,你可以很容易看出如何回答問題:

# 使其變bold的裝飾器
def makebold(fn):
    # 裝飾器將要返回的函數
    def wrapper():
        # 在原函數前面和后面插入一些代碼
        return "<b>" + fn() + "</b>"
    return wrapper

# 使其變italic的裝飾器
def makeitalic(fn):
      # 裝飾器將要返回的函數
    def wrapper():
        # 在原函數前面和后面插入一些代碼
        return "<i>" + fn() + "</i>"
    return wrapper

@makebold
@makeitalic
def say():
    return "hello"

print say() 
# 輸出為: <b><i>hello</i></b>

# 這和下面代碼效果相同
def say():
    return "hello"
say = makebold(makeitalic(say))

print say() 
# 輸出為: <b><i>hello</i></b>

你可以高興的離開這里了,或者再費點腦子來看看裝飾器的高級用法。


更深入的討論裝飾器

向被裝飾的函數傳參數

# 這不是黑魔法,你只需要讓封裝函數傳遞這些參數:

def a_decorator_passing_arguments(function_to_decorate):
    def a_wrapper_accepting_arguments(arg1, arg2):
        print "I got args! Look:", arg1, arg2
        function_to_decorate(arg1, arg2)
    return a_wrapper_accepting_arguments

# 因為當你調用被裝飾器返回的函數時,實際你是在調用封裝函數  
# 所以向封裝函數傳遞參數可以讓封裝函數把參數傳遞給被裝飾的函數

@a_decorator_passing_arguments
def print_full_name(first_name, last_name):
    print "My name is", first_name, last_name
    
print_full_name("Peter", "Venkman")
# 輸出為:
# I got args! Look: Peter Venkman
# My name is Peter Venkman

裝飾方法

Python中方法和函數幾乎是一樣的,這個特性很nice。唯一的不同是方法期望它的第一個參數是對當前對象的引用(self)。

那意味著你可以使用相同的方式來給方法添加裝飾器!只是需要將self考慮在內:

def method_friendly_decorator(method_to_decorate):
    def wrapper(self, lie):
        lie = lie - 3 # 很友好吧,再次減少了年齡 :-)
        return method_to_decorate(self, lie)
    return wrapper
    
    
class Lucy(object):
    
    def __init__(self):
        self.age = 32
    
    @method_friendly_decorator
    def sayYourAge(self, lie):
        print "I am %s, what did you think?" % (self.age + lie)
        
l = Lucy()
l.sayYourAge(-3)
# 輸出為: I am 26, what did you think?

如果你在寫一個通用的裝飾器--可以接收任何參數的函數或者方法--這時候只需要使用 *args, **kwargs:

def a_decorator_passing_arbitrary_arguments(function_to_decorate):
    # 封裝函數可以接收任何的參數
    def a_wrapper_accepting_arbitrary_arguments(*args, **kwargs):
        print "Do I have args?:"
        print args
        print kwargs
        # 然后你解包出參數,這里是 *args, **kwargs 
        # 如果你不熟悉怎么解包,可以查看:
        # http://www.saltycrane.com/blog/2008/01/how-to-use-args-and-kwargs-in-python/
        function_to_decorate(*args, **kwargs)
    return a_wrapper_accepting_arbitrary_arguments

@a_decorator_passing_arbitrary_arguments
def function_with_no_argument():
    print "Python is cool, no argument here."

function_with_no_argument()
#輸出為:
#Do I have args?:
#()
#{}
#Python is cool, no argument here.

@a_decorator_passing_arbitrary_arguments
def function_with_arguments(a, b, c):
    print a, b, c
    
function_with_arguments(1,2,3)
# 輸出為:
#Do I have args?:
#(1, 2, 3)
#{}
#1 2 3 
 
@a_decorator_passing_arbitrary_arguments
def function_with_named_arguments(a, b, c, platypus="Why not ?"):
    print "Do %s, %s and %s like platypus? %s" %\
    (a, b, c, platypus)

function_with_named_arguments("Bill", "Linus", "Steve", platypus="Indeed!")
# 輸出為:
#Do I have args ? :
#('Bill', 'Linus', 'Steve')
#{'platypus': 'Indeed!'}
#Do Bill, Linus and Steve like platypus? Indeed!

class Mary(object):
    
    def __init__(self):
        self.age = 31
    
    @a_decorator_passing_arbitrary_arguments
    def sayYourAge(self, lie=-3): # 這時候你可以添加一個默認參數值
        print "I am %s, what did you think ?" % (self.age + lie)

m = Mary()
m.sayYourAge()
# 輸出為:
# Do I have args?:
#(<__main__.Mary object at 0xb7d303ac>,)
#{}
#I am 28, what did you think?

給裝飾器傳遞參數

好了,現在你覺得給裝飾器本身傳遞參數該怎么做呢?

這個可能有點繞,因為裝飾器必須接收一個函數作為參數。
因此,你不能把被裝飾函數的參數直接傳遞給裝飾器。

在我們說出解決辦法前,寫點代碼來找找靈感:

# 裝飾器只是普通的函數
def my_decorator(func):
    print "I am an ordinary function"
    def wrapper():
        print "I am function returned by the decorator"
        func()
    return wrapper

# 因此,你可以不使用任何的 "@" 就可以調用它

def lazy_function():
    print "zzzzzzzz"

decorated_function = my_decorator(lazy_function)
# 輸出為: I am an ordinary function
            
# 它輸出 "I am an ordinary function",因為那就是你在代碼里面做的事情:  
# 調用一個函數,沒有任何的魔法。

@my_decorator
def lazy_function():
    print "zzzzzzzz"
    
# 輸出為: I am an ordinary function

上面兩種方式幾乎一樣。"my_decorator"被調用。因此當你在代碼里面添加 @my_decorato時,你就在告訴Python去調用'被"my_decorator"變量標示的函數'。

這很重要! 你給出的這個變量名可以直接指向裝飾器-也可以不直接指向

我們來干點邪惡的事情。 ?

def decorator_maker():
    
    print "I make decorators! I am executed only once: "+\
          "when you make me create a decorator."
            
    def my_decorator(func):
        
        print "I am a decorator! I am executed only when you decorate a function."
               
        def wrapped():
            print ("I am the wrapper around the decorated function. "
                  "I am called when you call the decorated function. "
                  "As the wrapper, I return the RESULT of the decorated function.")
            return func()
        
        print "As the decorator, I return the wrapped function."
        
        return wrapped
    
    print "As a decorator maker, I return a decorator"
    return my_decorator
            
# 我們創建了一個裝飾器。它就只是一個新的函數。
new_decorator = decorator_maker()       
# 輸出為:  
#I make decorators! I am executed only once: when you make me create a decorator.
#As a decorator maker, I return a decorator

# 然后我們裝飾一個函數       
def decorated_function():
    print "I am the decorated function."
   
decorated_function = new_decorator(decorated_function)
# 輸出為:
#I am a decorator! I am executed only when you decorate a function.
#As the decorator, I return the wrapped function
     
# 我們調用這個函數:
decorated_function()
# 輸出為:
#I am the wrapper around the decorated function. I am called when you call the decorated function.
#As the wrapper, I return the RESULT of the decorated function.
#I am the decorated function.

這里沒有任何驚奇的地方。

讓我們再次來做相同的事情,但是省略掉所有討厭的中間變量:

def decorated_function():
    print "I am the decorated function."
decorated_function = decorator_maker()(decorated_function)
# 輸出為:
#I make decorators! I am executed only once: when you make me create a decorator.
#As a decorator maker, I return a decorator
#I am a decorator! I am executed only when you decorate a function.
#As the decorator, I return the wrapped function.

# 最后:
decorated_function()    
# 輸出為:
#I am the wrapper around the decorated function. I am called when you call the decorated function.
#As the wrapper, I return the RESULT of the decorated function.
#I am the decorated function.

讓我們使它更簡潔:

@decorator_maker()
def decorated_function():
    print "I am the decorated function."
# 輸出為:
#I make decorators! I am executed only once: when you make me create a decorator.
#As a decorator maker, I return a decorator
#I am a decorator! I am executed only when you decorate a function.
#As the decorator, I return the wrapped function.

# 最終: 
decorated_function()    
# 輸出為:
#I am the wrapper around the decorated function. I am called when you call the decorated function.
#As the wrapper, I return the RESULT of the decorated function.
#I am the decorated function.

Hey,你注意到了嗎?我們除了 "@"格式的語法糖外還使用了函數調用! :-)

因此,回到帶參數裝飾器的討論。如果我們可以使用函數來創建裝飾器,我們就可以把參數傳遞給那個函數,對吧?

def decorator_maker_with_arguments(decorator_arg1, decorator_arg2):
    
    print "I make decorators! And I accept arguments:", decorator_arg1, decorator_arg2
            
    def my_decorator(func):
        # 這里之所有可以傳遞參數,得益于closures的特性。  
        # 如果你不熟悉closures,你可以假設這是沒問題的,
        # 或者讀一下: http://stackoverflow.com/questions/13857/can-you-explain-closures-as-they-relate-to-python  
        
        print "I am the decorator. Somehow you passed me arguments:", decorator_arg1, decorator_arg2
               
        # 不要把裝飾器的參數和函數的參數搞混
        def wrapped(function_arg1, function_arg2) :
            print ("I am the wrapper around the decorated function.\n"
                  "I can access all the variables\n"
                  "\t- from the decorator: {0} {1}\n"
                  "\t- from the function call: {2} {3}\n"
                  "Then I can pass them to the decorated function"
                  .format(decorator_arg1, decorator_arg2,
                          function_arg1, function_arg2))
            return func(function_arg1, function_arg2)
        
        return wrapped
    
    return my_decorator

@decorator_maker_with_arguments("Leonard", "Sheldon")
def decorated_function_with_arguments(function_arg1, function_arg2):
    print ("I am the decorated function and only knows about my arguments: {0}"
           " {1}".format(function_arg1, function_arg2))
          
decorated_function_with_arguments("Rajesh", "Howard")
# 輸出為:
#I make decorators! And I accept arguments: Leonard Sheldon
#I am the decorator. Somehow you passed me arguments: Leonard Sheldon
#I am the wrapper around the decorated function. 
#I can access all the variables 
#   - from the decorator: Leonard Sheldon 
#   - from the function call: Rajesh Howard 
#Then I can pass them to the decorated function
#I am the decorated function and only knows about my arguments: Rajesh Howard

這就是它了:帶參數的裝飾器。參數可以使用變量來設定

c1 = "Penny"
c2 = "Leslie"

@decorator_maker_with_arguments("Leonard", c1)
def decorated_function_with_arguments(function_arg1, function_arg2):
    print ("I am the decorated function and only knows about my arguments:"
           " {0} {1}".format(function_arg1, function_arg2))

decorated_function_with_arguments(c2, "Howard")
# 輸出為:
#I make decorators! And I accept arguments: Leonard Penny
#I am the decorator. Somehow you passed me arguments: Leonard Penny
#I am the wrapper around the decorated function. 
#I can access all the variables 
#   - from the decorator: Leonard Penny 
#   - from the function call: Leslie Howard 
#Then I can pass them to the decorated function
#I am the decorated function and only knows about my arguments: Leslie Howard

正如你看到的那樣,你可以使用這個技巧像給函數傳遞參數一樣給裝飾器傳遞參數。如果你想,你甚至可以使用 *args, **kwargs。但是記住裝飾器只會被調用一次。僅僅當Python載入(imports)這個腳本的時候調用。之后你不可以動態的設定參數。當你 "import x"的時候, 這個函數已經被裝飾了,因此你不能再改變任何東西了。



讓我們來練習一下:裝飾一個裝飾器

好了,作為福利,我將給你一個代碼片段,它可以讓裝飾器接收任何參數。
為了能接收參數,我們使用另一個函數來創建我們的裝飾器。

我們封裝了裝飾器。

我們最近知道的能封裝函數的東西是什么?
對,就是裝飾器

讓我們來寫一個裝飾裝飾器的裝飾器來玩玩(夠繞吧):

def decorator_with_args(decorator_to_enhance): 
    """  
    這個函數被當做裝飾器來使用。  
    它必須裝飾另一個函數,這個函數也被當做裝飾器使用
    感覺理解不過來,休息一下
    它允許任何的裝飾器接收任何參數。
    在你的腦子里面記住每次應該怎樣構建這樣的裝飾器
    """
    
    # 我們使用相同的技巧來傳遞參數
    def decorator_maker(*args, **kwargs):
        
        # 我們動態的創建一個接收一個函數作為參數的裝飾器  
        # 保持住從decorator_maker傳遞過來的參數。
        def decorator_wrapper(func):
       
            # 我們最后返回原始裝飾器的結果,  
            # 這個結果就是最原始被裝飾的函數 (就是返回一個函數)。
            # 這里只有一個小缺陷:被封裝的裝飾器必須具有特定的簽名,不然它不會工作
            return decorator_to_enhance(func, *args, **kwargs)這個樣子
        
        return decorator_wrapper
    
    return decorator_maker

它可以像下面這樣使用:

# 你創建一個將要被用作裝飾器的函數。并向它添加一個裝飾器(這里是指decorator_with_args) :-)
# 不要忘了你創建的這個裝飾器簽名必須是 "decorator(func, *args, **kwargs)"
@decorator_with_args 
def decorated_decorator(func, *args, **kwargs): 
    def wrapper(function_arg1, function_arg2):
        print "Decorated with", args, kwargs
        return func(function_arg1, function_arg2)
    return wrapper
    
# 之后你使用上面定義的裝飾器(指decorated_decorator)來裝飾一個函數
@decorated_decorator(42, 404, 1024)
def decorated_function(function_arg1, function_arg2):
    print "Hello", function_arg1, function_arg2

decorated_function("Universe and", "everything")
# 輸出為:
#Decorated with (42, 404, 1024) {}
#Hello Universe and everything

# 喔!

我知道,你有了這樣一種感覺,它是在聽一一個家伙說:"before understanding recursion, you must first understand recursion"產生的。但是,現在,你是否有更好的體會了?


最佳實踐: 裝飾器

  • 裝飾器是Python2.4引入的,因此保證你的代碼運行的版本 >= 2.4。
  • 裝飾器會拖慢函數的執行速度,記住這點。
  • 你不能反裝飾一個函數。因此一旦一個函數被裝飾了,它對所有其它代碼來說就都是被裝飾了的。
  • 裝飾器會封裝函數,這會讓它們變得更難調試。 (這點從Python >= 2.5版本變得好了起來; 具體參考下面。)

在Python 2.5中 functools模塊被引入。它包含了 functools.wraps()函數,這個函數會將被裝飾函數的名稱、模塊、文檔字符串拷貝到封裝函數。

(有趣的事實是: functools.wraps() 也是一個裝飾器! ?)

# 為了調試,在堆棧軌跡中打印了函數的名稱(__name__)
def foo():
    print "foo"
    
print foo.__name__
# 輸出為 : foo
    
# 有了裝飾器,名稱就凌亂了 
def bar(func):
    def wrapper():
        print "bar"
        return func()
    return wrapper

@bar
def foo():
    print "foo"

print foo.__name__
# 輸出為: wrapper

# "functools"對這個有幫助

import functools

def bar(func):
    # 我們看到"wrapper"封裝了"func"
    # 現在魔法開始了
    @functools.wraps(func)
    def wrapper():
        print "bar"
        return func()
    return wrapper

@bar
def foo():
    print "foo"

print foo.__name__
# 輸出為: foo

裝飾器可以被用在什么地方?

現在問題來了:我可以使用裝飾器來干什么?

盡管看起來很酷很強大,但是一個實際的應用事例能更好的說明問題。好了,這里有1000種可能性。經典的使用是在庫以外的代碼中擴展一個函數的行為 (你不能修改的函數),或者只是為了調試 (因為調試只是零時的,所有你不想修改這個函數)。

你可以以一種DRY的方式使用他們來擴展許多函數,像這樣:

def benchmark(func):
    """
    打印原函數調用時間的裝飾器
    """
    import time
    def wrapper(*args, **kwargs):
        t = time.clock()
        res = func(*args, **kwargs)
        print func.__name__, time.clock()-t
        return res
    return wrapper


def logging(func):
    """
    記錄腳本行為日志的裝飾器  
    (這里只是打印它,但是也可以記錄到日志里面!)
    """
    def wrapper(*args, **kwargs):
        res = func(*args, **kwargs)
        print func.__name__, args, kwargs
        return res
    return wrapper


def counter(func):
    """
    記錄并打印一個函數執行次數的裝飾器
    """
    def wrapper(*args, **kwargs):
        wrapper.count = wrapper.count + 1
        res = func(*args, **kwargs)
        print "{0} has been used: {1}x".format(func.__name__, wrapper.count)
        return res
    wrapper.count = 0
    return wrapper

@counter
@benchmark
@logging
def reverse_string(string):
    return str(reversed(string))

print reverse_string("Able was I ere I saw Elba")
print reverse_string("A man, a plan, a canoe, pasta, heros, rajahs, a coloratura, maps, snipe, percale, macaroni, a gag, a banana bag, a tan, a tag, a banana bag again (or a camel), a crepe, pins, Spam, a rut, a Rolo, cash, a jar, sore hats, a peon, a canal: Panama!")

#輸出為:
#reverse_string ('Able was I ere I saw Elba',) {}
#wrapper 0.0
#wrapper has been used: 1x 
#ablE was I ere I saw elbA
#reverse_string ('A man, a plan, a canoe, pasta, heros, rajahs, a coloratura, maps, snipe, percale, macaroni, a gag, a banana bag, a tan, a tag, a banana bag again (or a camel), a crepe, pins, Spam, a rut, a Rolo, cash, a jar, sore hats, a peon, a canal: Panama!',) {}
#wrapper 0.0
#wrapper has been used: 2x
#!amanaP :lanac a ,noep a ,stah eros ,raj a ,hsac ,oloR a ,tur a ,mapS ,snip ,eperc a ,)lemac a ro( niaga gab ananab a ,gat a ,nat a ,gab ananab a ,gag a ,inoracam ,elacrep ,epins ,spam ,arutaroloc a ,shajar ,soreh ,atsap ,eonac a ,nalp a ,nam A

當然,裝飾器最好的一點是你不需要重寫,就可以幾乎在任何東西上面使用它們。這就是我所說的,DRY:

@counter
@benchmark
@logging
def get_random_futurama_quote():
    from urllib import urlopen
    result = urlopen("http://subfusion.net/cgi-bin/quote.pl?quote=futurama").read()
    try:
        value = result.split("<br><b><hr><br>")[1].split("<br><br><hr>")[0]
        return value.strip()
    except:
        return "No, I'm ... doesn't!"

    
print get_random_futurama_quote()
print get_random_futurama_quote()

#outputs:
#get_random_futurama_quote () {}
#wrapper 0.02
#wrapper has been used: 1x
#The laws of science be a harsh mistress.
#get_random_futurama_quote () {}
#wrapper 0.01
#wrapper has been used: 2x
#Curse you, merciful Poseidon!

Python語言本身提供了好多種裝飾器:property, staticmethod,等等。

  • Django使用裝飾器來管理緩存和視圖權限。
  • Twisted to fake inlining asynchronous functions calls.(對Twisted不熟,不知道具體講的是神馬!)。

這個真是一個巨大的游樂場

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 要點: 函數式編程:注意不是“函數編程”,多了一個“式” 模塊:如何使用模塊 面向對象編程:面向對象的概念、屬性、...
    victorsungo閱讀 1,587評論 0 6
  • Python進階框架 希望大家喜歡,點贊哦首先感謝廖雪峰老師對于該課程的講解 一、函數式編程 1.1 函數式編程簡...
    Gaolex閱讀 5,534評論 6 53
  • http://python.jobbole.com/85231/ 關于專業技能寫完項目接著寫寫一名3年工作經驗的J...
    燕京博士閱讀 7,631評論 1 118
  • 前言 Python的修飾器的英文名叫Decorator,當你看到這個英文名的時候,你可能會把其跟Design Pa...
    linheimx閱讀 637評論 0 4
  • 零碎記錄。 2017年8月26日星期六 昨天辦理了出院手續,身體不適的癥狀漸漸好起來。這段時間自己懶惰了,雖然也想...
    若蘭幽香閱讀 236評論 0 2