возвращение пользовательского имени функции при использовании декоратора с вызываемым объектом - PullRequest
2 голосов
/ 29 мая 2011

Рассмотрим следующий фрагмент кода.

def print_timing(func):
    import time
    def wrapper(*args, **kwargs):
        t1 = time.time()
        res = func(*args, **kwargs)
        t2 = time.time()
        print '%s took %0.3f s ~ %0.0f min and %0.1f sec' % (func.func_name, t2-t1, int(t2 - t1)/60, (t2-t1) % 60 )
        return res
    return wrapper

@print_timing                                                                      |
def foo():                                                                         |
    return 'foo' 

class name(object):
       def __init__(self, name):
              self.name = name
       @print_timing
       def __call__(self):
              return self.name

bar = name("bar")
print bar()

Возвращает:

__call__ took 0.000 s ~ 0 min and 0.0 sec
bar

Объект bar ведет себя как функция с именем bar, но предоставляет подробности внутренней реализации__call__ при использовании с декоратором print_timing.Есть ли способ изменить объект name (возможно, передав подходящий аргумент функции __init__), чтобы он вместо этого возвращал

 bar took 0.000 s ~ 0 min and 0.0 sec

?Мне нужно решение, которое позволит декоратору print_timing продолжать работать с обычными функциями.Запуск print foo() дает

foo took 0.000 s ~ 0 min and 0.0 sec
foo

Ответы [ 3 ]

1 голос
/ 29 мая 2011

Пока вы используете декоратор только для методов, они будут передаваться self в качестве первого аргумента:

def print_timing(func):
    import time
    def wrapper(*args, **kwargs):
        t1 = time.time()
        res = func(*args, **kwargs)
        t2 = time.time()
        funcname = func.__name__
        # Special case; a "name" instance has a "name" attribute we want to use instead.
        if len(args) >= 1 and isinstance(args[0], name):
            funcname = args[0].name
        print '%s took %0.3f s ~ %0.0f min and %0.1f sec' % (funcname, t2-t1, int(t2 - t1)/60, (t2-t1) % 60 )
        return res
    return wrapper

Обновлено : Оболочка теперь использует func.__name__ по умолчанию, но если вы используете это для класса name (как в исходном вопросе), она будет использовать атрибут name экземпляр вместо.

Я использовал тест isinstance, чтобы определить, присутствует ли атрибут name, но вместо этого вы могли бы использовать типизацию с уткой (if hasattr(args[0], 'name')); переменная name настолько универсальна, что вы, скорее всего, получите неожиданные результаты при использовании с произвольными методами класса.

1 голос
/ 29 мая 2011

Используйте @print_timing в качестве декоратора класса:

@print_timing
class name(object):
    ...

Никаких изменений не требуется;ваш обернутый объект теперь является функцией, когда он должен был быть классом, но я понимаю из вашего вопроса (и факта, что это вызываемый класс), что на самом деле это не имеет значения (если это так, вы можете изменить декоратор насделать возвращенный упакованный объект "красивее").

1 голос
/ 29 мая 2011

Нет.Декоратор происходит, когда создается класс , а вызов __init__() происходит, когда создается экземпляр .Вам нужно было бы, чтобы декоратор превратил функцию в дескриптор, и чтобы этот дескриптор получил имя из экземпляра.

...