由淺入深分析python修飾器

2021-07-24 12:39:39 字數 4359 閱讀 2978

單例模式的設計中有種方法,通過修飾器設計:

def

singleton

(cls, *args, **kw):

instances = {}

defgetinstance

():if cls not

in instances:

instances[cls] = cls(*args, **kw)

return instances[cls]

return getinstance

@singleton

class

myclass:

...

複習加深入思考單例模式的設計思路與原理

先打乙個比方,乙個玻璃杯子,我們用來裝水(功能),有一天,我們想裝熱水(增強後的功能),那我們需要在杯子外面套乙個皮套(額外的功能),這時,皮套就起到了裝飾器的功能,他提供了隔熱的效果,使杯子有了裝熱水的功能。而且,這個皮套能夠通用的套在不同的杯子上,達到同樣的效果。

回到我們的問題,裝飾器根本上也是一種方法,他形式上簡單易用,可以反覆應用在不同的函式上,比如:輸出日誌,列印呼叫時間。裝飾器在一定程度上提取了復用的**,增強原函式的功能。

分析完了為什麼我們使用修飾器,我們現在從頭開始分析修飾器是怎樣形成的。

1,現在,我們有乙個函式 hello

def

hello

():print

'hello world'

2,我們想新增乙個功能,讓他列印呼叫的時間,很簡單,加入一行**就夠了(為了簡單,本文省略import time語句)

def

hello

():print time.time()

print

'hello world'

3,我們的效果達到了,但是,如果有fun1,fun2,fun3也需要達到同樣的效果,我們難道要每個函式都修改?這顯然增加了毫無意義的勞動。我們何不專門定義乙個函式,用來列印呼叫時間並執行函式

def call_time(fun):

print

time.time()

hello()

def hello():

print

'hello world'

call_time(hello)

4,現在雖然目的達到了,但是如果在程式中顯然更改了業務流程,本來呼叫hello函式,卻變成了呼叫call_time函式(如果hello函式有引數,顯然這種方法不可取)

5,我們可以定義乙個函式,其功能為返回增強後的功能

def call_time(fun):

print

time.time()

return hello() #這裡新增return,如果hello有返回值則將其返回

def hello():

print

'hello world'

hello = call_time(hello)

hello()

python中定義了修飾器的語法規則,上述**等同於:

def call_time(fun):

...@call_time

def hello():

print 'hello world'

hello()

6,如果我們的hello函式有乙個引數,輸入乙個姓名,輸出打招呼

def

hello

(str):

print

'hello %s'%str

這是我們修改一下**,新增接收引數,乙個簡單的修飾器就形成了

def call_time(fun):

print

time.time()

return hello(*args, **kw)

@call_time

def hello(str):

print

'hello %s'

%str

hello('timorchow')

有時候我們需要給修飾器的功能傳遞一些引數,只要在現有的修飾器外加裝一層接收引數的函式就好了

def call_time_args(str):

def call_time(fun):

print str,time.time()

return hello(*args, **kw)

return call_time

@call_time_args('now time')

def hello():

print

'hello'

hello()

結果為:

now time:1468135648943

hello

再來看看類裝飾器,相比函式裝飾器,類裝飾器具有靈活度大、高內聚、封裝性等優點。使用類裝飾器還可以依靠類內部的__call__方法,當使用 @ 形式將裝飾器附加到函式上時,就會呼叫此方法。

class

foo(object):

def__init__

(self, func):

self._func = func

def__call__

(self):

print ('class decorator runing')

self._func()

print ('class decorator ending')

@foo

defbar

():print ('bar')

bar()

使用裝飾器極大地復用了**,但是他有乙個缺點就是原函式的元資訊不見了,比如函式的docstring、name、引數列表,先看例子:

裝飾器

def

logged

(func):

defwith_logging

(*args, **kwargs):

print func.__name__ + " was called"

return func(*args, **kwargs)

return with_logging

函式

@logged

deff

(x):

"""does some math"""

return x + x * x

該函式完成等價於:

def

f(x):

"""does some math"""

return x + x * x

f = logged(f)

不難發現,函式f被with_logging取代了,當然它的

docstring,__name__就是變成了with_logging函式的資訊了。

print f.__name__ # prints 'with_logging'

print f.__doc__ # prints none

這個問題就比較嚴重的,好在我們有functools.wraps,wraps本身也是乙個裝飾器,它能把原函式的元資訊拷貝到裝飾器函式中,這使得裝飾器函式也有和原函式一樣的元資訊了。

from functools import wraps

deflogged

(func):

@wraps(func)

defwith_logging

(*args, **kwargs):

print func.__name__ + " was called"

return func(*args, **kwargs)

return with_logging

@logged

deff

(x):

"""does some math"""

return x + x * x

print f.__name__ # prints 'f'

print f.__doc__ # prints 'does some math'

內建@staticmathod、@classmethod、@property的順序

@a

@b@c

deff

():

等效於

f =a(b(c(f)))
參考文章:

由淺入深分析阻塞佇列

arraybolckingqueue 由陣列結構組成的有界阻塞佇列。內部構造是陣列,具有陣列的特性。public arrayblockingqueue int capacity public arrayblockingqueue int capacity,boolean fair 可以初始化佇列 且...

python中 修飾器

參考文章 python中 修飾符 示例如下 def test func func test deffun print call fun 上面的 會輸出 call fun 修飾符有點像函式指標,python直譯器發現執行的時候如果碰到 修飾的函式,首先就解析它,找到它對應的函式進行呼叫,並且會把 修飾...

python 修飾器作用

在python的函式中,函式定義的上一行有 functionname的修飾,當直譯器讀到 這樣的修飾符之後,會先解析 後的內容,把 下一行的函式或者類作為 後邊的函式的引數,然後將返回值賦值給下一行修飾的函式物件。比如 a bdef c deffunca a print in func a deff...