1. 程式人生 > >Python裝飾器模式學習總結

Python裝飾器模式學習總結

裝飾器模式,重點在於裝飾。裝飾的核心仍舊是被裝飾物件。

類比於Java程式設計的時候的包裝模式,是同樣的道理。雖然概念上稍有不同但是原理上還是比較相近的。下面我就來談一談我對Python的裝飾器的學習的一點心得吧。


關於作用域

Python作用域 體現在LEGB中:

  • L:local 函式內部
  • E: enclosing 函式內部和內建函式之間
  • G:global 全域性性質,有名稱空間的限制
  • B:build-in 內建函式,由python直譯器管理

學過程式設計的人一眼就可以看得出來大致是什麼意思,所以這裡不再敘述。


關於閉包

關鍵在於理解Python中閉包的概念。說白了,閉包就是函式裡面又定義了函式,這就是閉包了。(呵呵,斷章取義一下。可能說法不恰當)。如下:

def func1():
	print 'func1 is running'
	def in_func1():
		print 'in_func1 is running'
		return in_func1
	print 'over'

簡單的一個閉包的實現,就差不多是這個樣子的。我們需要注意的就是要將內部函式當成一個物件來返回(Python中函式其實就是一個物件,我們可以通過type來進行驗證)。
這段程式碼執行的流程是先執行func1,然後執行func2,並且將func2作為一個屬性返回給func1.這樣我們可以再次的得到func2的內容。這就是閉包!


關於裝飾器

類比Java中那麼多的模式,比如ServletRequest被裝飾成了HttpServletRequest。Python中也有很多這樣被裝飾的例子。如CookieJar被裝飾成了MozillaCookieJar等等。實現的功能就是被裝飾物件的功能得到了增強,完成的效果也大大大的比未裝飾之前好了。這就是裝飾的好處。
下面就來看一看Python中怎麼來實現裝飾器吧。

核心:藉助於@符號,即可。

def bar(func):
	print 'Bar'
	return func

@bar
def foo():
    print "foo"
# 其等價於:
def foo():
    print "foo"
foo = bar(foo)

程式碼執行的流程:
先執行@物件,也就是一個函式。其返回值就是一個內建函式,只不過這個內建函式是得到了裝飾的被裝飾物件(這裡是foo函式),我們可以理解為:

裝飾器,其本身接收一個函式物件作為引數,然後做一些工作後,返回接收的引數,供外界呼叫。

下面看一個例項:

import time

def function_performance_statistics(trace_this=True):
    if trace_this:
       def performace_statistics_delegate(func):
            def counter(*args, **kwargs):
                start = time.clock()
                func(*args, **kwargs)
                end =time.clock()
                print 'used time: %d' % (end - start, )
            return counter
    else:
       def performace_statistics_delegate(func):
            return func
    return performace_statistics_delegate

@function_performance_statistics(True)
def add(x, y):
    time.sleep(3)
    print 'add result: %d' % (x + y,)

@function_performance_statistics(False)
def mul(x, y=1):
    print 'mul result: %d' % (x * y,)

add(1, 1)
mul(10)
上述程式碼想要實現一個性能分析器,並接收一個引數,來控制性能分析器是否生效,其執行效果如下所示:
add result: 2
used time: 0
mul result: 10
上述程式碼中裝飾器的呼叫等價於:
add = function_performance_statistics(True)(add(1, 1))
mul = function_performance_statistics(False)(mul(10))

寫一個日誌記錄器

# 做一個logger
def logging(filename="", level=0):
    def wrapper(func):
        def log(*args, **kwargs):
            with open(filename, "a") as file:
                line = "[LEVEL: {}] {}, {}\n".format(level, args, kwargs)
                file.write(line)
                file.close()
            # 需要有返回值就return,否則就不用return
            return func(*args, **kwargs)
        return log
    return wrapper

@logging(filename="query.log", level=1)
def query(msg):
    print(msg, " be carried.")
    return "`{}` has beed logged.".format(msg)

print(query("select * from user where userid = 2614677"))

總結

Python裝飾器的核心就是裝飾,實質就是被裝飾函式效能的增強。