2014-07-21 33 views
0

我正在爲每個呼叫之間至少需要等待1秒的API構建包裝。我以爲我可以解決這個問題以下列方式使用裝飾:保證與裝飾者之間的電話間的最小等待時間

import datetime, time 

last_time = datetime.datetime(2014, 1, 1) 

def interval_assurer(f): 
    global last_time 
    if (datetime.datetime.now() - last_time).seconds < 1: 
     print("Too fast...") 
     time.sleep(1) 

    last_time = datetime.datetime.now() 

    return f 

@interval_assurer 
def post(): 
    pass 

這不會工作,雖然,出於某種原因,我不知道爲什麼。 last_time在第一次調用post時得到更新,但之後不會更新。請記住,這是我第一次嘗試裝飾器,所以我可能錯過了一些基本的東西。

謝謝。

回答

2

裝飾器函數只是返回原始函數,因此只有當裝飾器被調用時,即計算函數定義時纔會運行時間代碼。相反,它應該返回一個新功能,它包含時間碼並在適當時調用f

嘗試類似:

def interval_assurer(f): 
    def func(): 
     global last_time 
     if (datetime.datetime.now() - last_time).seconds < 1: 
      print("Too fast...") 
      time.sleep(1) 
     last_time = datetime.datetime.now() 
     return f() 
    return func 

如果你的裝飾功能需要的參數,你應該包括在func的定義和調用f*args, **kwargs;另外,請考慮用functools.wraps(f)依次裝飾func


大廈@變成真正的答案,就可以使last_time包裝功能的屬性刪除global(允許多個包裹功能,每個都有自己的定時器),甚至使一個裝飾在一個需要參數爲間隔執行:

import functools 
import time 

def interval_assured(interval): 
    """Ensure consecutive calls are separated by a minimal interval.""" 
    def wrapper(f): 
     @functools.wraps(f) 
     def func(*args, **kwargs): 
      if (time.time() - func.last_time) < interval: 
       time.sleep(interval) 
      result = f(*args, **kwargs) 
      func.last_time = time.time() 
      return result 
     func.last_time = time.time() 
     return func 
    return wrapper 

注意的時間是後重置包裝的函數f被稱爲 - 這是重要的,如果的運行時相對於間隔來說很大。

在使用中:

>>> def testing(x): 
    print time.time() 
    print x 


>>> for x in range(3): 
    testing(x) 


1405938405.97 
0 
1405938406.01 
1 
1405938406.02 
2 
>>> @interval_assured(5) 
def testing(x): 
    print time.time() 
    print x 


>>> for x in range(3): 
    testing(x) 


1405938429.71 
0 
1405938434.73 
1 
1405938439.75 
2 
+0

我明白了!太好了謝謝。 –

+0

我編輯過,用Python的修飾器的一些更高級特性添加更復雜的實現。 – jonrsharpe

4

由於您的interval_assurer是一個裝飾,它調用一次:在函數定義的時候,而不是當它被稱爲。你需要建立一個包裝功能部件是這樣的:

import time, functools 

def interval_assurer(f): 
    last_time = [0] 
    @functools.wraps(f) # optional, but nice to have 
    def wrapper(*args, **kwargs): 
     time_diff = time.time() - last_time[0] 
     if time_diff < 1: 
      print("Too fast...") 
      time.sleep(1 - time_diff) 

     last_time[0] = time.time() 
     return f(*args, **kwargs) 

    return wrapper 

@interval_assurer 
def post(self, **kwargs): 
    pass 

你也不會需要全球則(招用​​列表可以nonlocal在Python 3所取代)。