Добавление метода к объекту функции во время выполнения

Ранее я читал вопрос о том, есть ли в Python метод times, который позволял бы вызывать функцию n раз подряд.

Все предлагали for _ in range(n): foo(), но я хотел попробовать написать другое решение, используя декоратор функций.

Вот что у меня есть:

def times(self, n, *args, **kwargs):
    for _ in range(n):
        self.__call__(*args, **kwargs)

import new
def repeatable(func):
    func.times = new.instancemethod(times, func, func.__class__)

@repeatable
def threeArgs(one, two, three):
    print one, two, three

threeArgs.times(7, "one", two="rawr", three="foo")

Когда я запускаю программу, я получаю следующее исключение:

Traceback (most recent call last):
  File "", line 244, in run_nodebug
  File "C:\py\repeatable.py", line 24, in 
    threeArgs.times(7, "one", two="rawr", three="foo")
AttributeError: 'NoneType' object has no attribute 'times'

Я так понимаю, декоратор не сработал? Как я могу это исправить?


person Carson Myers    schedule 17.04.2010    source источник
comment
Этот метод кажется менее идиоматичным и менее простым, чем тот, который вы заменяете.   -  person Mike Graham    schedule 18.04.2010


Ответы (3)


Ваш декоратор должен вернуть объект функции:

def repeatable(func):
    func.times = new.instancemethod(times, func, func.__class__)
    return func

Теперь он ничего не возвращает, поэтому вы фактически меняете threeArgs в None

Это потому что это:

@decorator
def func(...):
    ...

более-менее похоже на:

def func(...):
    ....
func = decorator(func)
person KillianDS    schedule 17.04.2010
comment
круто, наверное, я должен был догадаться... ну что ж, спасибо за помощь - person Carson Myers; 18.04.2010

Вам не хватает оператора return func в конце декоратора repeatable.

person Alex Martelli    schedule 17.04.2010

Рассматривали ли вы возможность не добавлять его в определенные функции и вместо этого разрешить его использование с любой функцией?

def times(n, func, *args, **kwds):
  return [func(*args, **kwds) for _ in xrange(n)]

(Я возвращаю список возвращаемых значений, но вы можете написать его, чтобы игнорировать их, подобно циклу for, который у вас есть в вопросе.)

Затем, где бы вы, с вашей версией, использовали:

threeArgs.times(7, "one", two="rawr", three="foo")

Вместо этого вы используете:

times(7, threeArgs, "one", two="rawr", three="foo")
person Community    schedule 18.04.2010