我想寫一個裝飾將限制次數可以執行的功能,以及語法如下一些:這些類型的python裝飾器是如何編寫的?
@max_execs(5)
def my_method(*a,**k):
# do something here
pass
我認爲這是可能寫這種類型的裝飾,但我不不知道如何。我認爲一個函數不會是這個裝飾器的第一個參數,對吧?我想要一個「普通的裝飾器」的實現,而不是一些有調用方法的類。
原因是要了解它們是如何書寫的。請解釋語法,以及裝飾器的工作原理。
我想寫一個裝飾將限制次數可以執行的功能,以及語法如下一些:這些類型的python裝飾器是如何編寫的?
@max_execs(5)
def my_method(*a,**k):
# do something here
pass
我認爲這是可能寫這種類型的裝飾,但我不不知道如何。我認爲一個函數不會是這個裝飾器的第一個參數,對吧?我想要一個「普通的裝飾器」的實現,而不是一些有調用方法的類。
原因是要了解它們是如何書寫的。請解釋語法,以及裝飾器的工作原理。
這是我鞭打。它不使用類,但它確實使用功能屬性:
def max_execs(n=5):
def decorator(fn):
fn.max = n
fn.called = 0
def wrapped(*args, **kwargs):
fn.called += 1
if fn.called <= fn.max:
return fn(*args, **kwargs)
else:
# Replace with your own exception, or something
# else that you want to happen when the limit
# is reached
raise RuntimeError("max executions exceeded")
return wrapped
return decorator
max_execs
返回運作稱爲decorator
,這反過來又返回wrapped
。decoration
在兩個函數屬性中存儲最大執行數和當前exec數,然後在wrapped
中進行檢查。
翻譯:當使用像這樣的裝飾:
@max_execs(5)
def f():
print "hi!"
你基本上做這樣的事情:
f = max_execs(5)(f)
我知道你說過你不想上課,但不幸的是,這是我能想到如何從頭頂開始做的唯一方法。
class mymethodwrapper:
def __init__(self):
self.maxcalls = 0
def mymethod(self):
self.maxcalls += 1
if self.maxcalls > 5:
return
#rest of your code
print "Code fired!"
火起來像這樣
a = mymethodwrapper
for x in range(1000):
a.mymethod()
輸出將是:
>>> Code fired!
>>> Code fired!
>>> Code fired!
>>> Code fired!
>>> Code fired!
這不能用於任何方法,並使用可調用的對象。我想要一個普通的裝飾者。 – Geo 2009-07-09 20:28:36
裝飾僅僅是一種把函數轉換成別的東西調用。在你的情況下,max_execs(5)
必須是一個可調用函數,它將函數轉換爲另一個可調用的對象,該對象將計算和轉發調用。
class helper:
def __init__(self, i, fn):
self.i = i
self.fn = fn
def __call__(self, *args, **kwargs):
if self.i > 0:
self.i = self.i - 1
return self.fn(*args, **kwargs)
class max_execs:
def __init__(self, i):
self.i = i
def __call__(self, fn):
return helper(self.i, fn)
我不明白你爲什麼會想限制自己的功能(而不是類)。但如果你真的想...
def max_execs(n):
return lambda fn, i=n: return helper(i, fn)
有兩種方法可以做到這一點。面向對象的方法是讓一個類:
class max_execs:
def __init__(self, max_executions):
self.max_executions = max_executions
self.executions = 0
def __call__(self, func):
@wraps(func)
def maybe(*args, **kwargs):
if self.executions < self.max_executions:
self.executions += 1
return func(*args, **kwargs)
else:
print "fail"
return maybe
爲wraps
的說明,請參見this question。
我更喜歡上面這種裝飾器的OOP方法,因爲你基本上有一個跟蹤執行次數的私有計數變量。但是,另一種方法是使用閉包,如
def max_execs(max_executions):
executions = [0]
def actual_decorator(func):
@wraps(func)
def maybe(*args, **kwargs):
if executions[0] < max_executions:
executions[0] += 1
return func(*args, **kwargs)
else:
print "fail"
return maybe
return actual_decorator
這涉及三個功能。 max_execs
函數被賦予一個執行次數的參數,並返回一個裝飾器,它將限制你進行那麼多的調用。該功能actual_decorator
與OOP示例中的__call__
方法具有相同的功能。唯一不可思議的是,由於我們沒有私有變量的類,因此我們需要修改位於閉包外部範圍內的executions
變量。 Python 3.0支持nonlocal
聲明,但在Python 2.6或更早版本中,我們需要將我們的執行計數包含在列表中,以便它可以進行變異。
如果例如具有@logged屬性的方法表示method = logged(method),那麼具有@max_execs(5)「方法」的方法是如何「翻譯」的? – Geo 2009-07-09 20:31:05
這和應用這個裝飾器時說的max_execs(5)(f) – 2009-07-09 20:37:41
不依靠這樣的狀態,一類,你必須保存功能本身的狀態(計數):
def max_execs(count):
def new_meth(meth):
meth.count = count
def new(*a,**k):
meth.count -= 1
print meth.count
if meth.count>=0:
return meth(*a,**k)
return new
return new_meth
@max_execs(5)
def f():
print "invoked"
[f() for _ in range(10)]
它給出:
5
invoked
4
invoked
3
invoked
2
invoked
1
invoked
0
-1
-2
-3
-4
此方法不會修改函數內部,而是將其包裝到可調用對象中。
與使用修補功能相比,使用class減慢了執行20%!
def max_execs(n=1):
class limit_wrapper:
def __init__(self, fn, max):
self.calls_left = max
self.fn = fn
def __call__(self,*a,**kw):
if self.calls_left > 0:
self.calls_left -= 1
return self.fn(*a,**kw)
raise Exception("max num of calls is %d" % self.i)
def decorator(fn):
return limit_wrapper(fn,n)
return decorator
@max_execs(2)
def fun():
print "called"
一樣,python代碼是如何「翻譯」的?例如,如果我的方法被稱爲blabla,並且我應用了max_execs屬性,那麼Python將如何看待它? blabla = max_execs(5)(blabla)? – Geo 2009-07-09 20:35:05