2014-01-04 2 views
2

Я хотел бы написать декоратор, который ограничивает количество вызовов обернутой функции. Скажем, если я хочу, чтобы завернутая функция была вызвана максимум 10 раз, декоратор должен выполнить эту функцию первые 10 раз, тогда она должна вместо этого вернуть None.Декодер Python для ограничения числа вызовов

Вот что я придумал:

from functools import wraps 


def max_calls(num): 
    """Decorator which allows its wrapped function to be called `num` times""" 

    def decorator(func): 
     @wraps(func) 
     def wrapper(*args, **kwargs): 
      calls = getattr(wrapper, 'calls', 0) 
      calls += 1 

      if calls == num: 
       return None 

      setattr(wrapper, 'calls', calls) 
      return func(*args, **kwargs) 
     setattr(wrapper, 'calls', 0) 
     return wrapper 
    return decorator 

Однако этот счетчик вызовов правильно, возвращает None при достижении предела, но ... это не сбрасывается между работает программа. То есть, если я выполняю программу один раз, счетчик достигает 5, а затем повторно запускает программу, она продолжается с 5. Что мне нужно изменить, чтобы декоратор работал правильно?

+0

На моей машине счетчик сбрасывается, как ожидалось, когда я запускаю сценарий во второй раз. Как вы выполняете свой скрипт? Я предполагаю, что вы не выполняете 'python myscript.py' из командной строки. – Kevin

+0

'То есть, если я выполняю программу один раз, счетчик достигает 5, а затем повторно запускает программу, она продолжается с 5' .. не могли бы вы объяснить, как вы выполняете программу? – Abhijit

+0

Вы работаете внутри интерпретатора python? –

ответ

1

Проблема в том, что вы поддерживаете только один набор вызовов. Но это означает, что каждый запрос на флягу делится кол-вом вызовов со всеми другими запросами. Что вам нужно сделать, так это поддерживать отдельный набор счетчиков вызовов для каждого запроса на флягу.

От чтения API documentation это выглядит, как если бы способ сделать эту работу, чтобы выполнить эти три шага:

  1. Сделайте подкласс flask.Request, который может хранить ваши счетчики вызовов функции:

    import collections 
    import flask 
    
    class MyRequest(flask.Request): 
        """A subclass of Request that maintains function call counts.""" 
        def __init__(self, *args, **kwargs): 
         super(MyRequest, self).__init__(*args, **kwargs) 
         self.call_counts = collections.defaultdict(int) 
    
  2. Set request_class в подклассе при инициализации приложения:

    app = flask.Flask(__name__, request_class=MyRequest, ...) 
    
  3. Перепишите ваш декоратор хранить свои счетчики в глобальном flask.request объекта:

    import functools 
    
    def max_calls(num, default=None): 
        """Decorator which allows its wrapped function to be called at most `num` 
        times per Flask request, but which then returns `default`. 
    
        """ 
        def decorator(func): 
         @functools.wraps(func) 
         def wrapper(*args, **kwargs): 
          if flask.request.call_counts[func] == num: 
           return default 
          flask.request.call_counts[func] += 1 
          return func(*args, **kwargs) 
         return wrapper 
        return decorator 
    

Но, написав, что, было бы упущением для меня не указать на то, что ваш вопрос кажется очень странным. Почему вы хотите ограничить количество случаев, когда функция может быть вызвана запросом на флягу? Вы пытаетесь сделать какое-то ограничение скорости? Кажется вероятным, что все, что вы хотите сделать, может быть сделано лучше, используя другой подход.

+0

Это звучит довольно странно, но это лучшее решение для моей проблемы: обернутая функция вызывает вызовы API Soundcloud для извлечения песни по заданному поисковому запросу (при этом условия поиска меняются от одного вызова к другому). Если поиск выполняется не более 10 раз, моя функция должна возвращать 'None' вместо бесконечной попытки поиска API с разными поисковыми терминами. На самом деле, это не совсем бесконечно, потому что в какой-то момент оно в конечном итоге вернет что-то, но этот предел действует скорее как тайм-аут, поддерживая время запроса с разумным пределом. – linkyndy

Смежные вопросы