Самоанализ, чтобы получить имена декораторов по методу? - PullRequest
29 голосов
/ 13 июля 2010

Я пытаюсь выяснить, как получить имена всех декораторов для метода. Я уже могу получить имя метода и строку документации, но не могу понять, как получить список декораторов.

Ответы [ 7 ]

33 голосов
/ 03 июля 2015

Я удивлен, что этот вопрос настолько старый, и никто не потратил время, чтобы добавить действительный интроспективный способ сделать это, поэтому вот он:

Код, который вы хотите проверить ...

def template(func):
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper

baz = template
che = template

class Foo(object):
    @baz
    @che
    def bar(self):
        pass

Теперь вы можете проверить вышеупомянутый класс Foo с помощью чего-то вроде этого ...

import ast
import inspect

def get_decorators(cls):
    target = cls
    decorators = {}

    def visit_FunctionDef(node):
        decorators[node.name] = []
        for n in node.decorator_list:
            name = ''
            if isinstance(n, ast.Call):
                name = n.func.attr if isinstance(n.func, ast.Attribute) else n.func.id
            else:
                name = n.attr if isinstance(n, ast.Attribute) else n.id

            decorators[node.name].append(name)

    node_iter = ast.NodeVisitor()
    node_iter.visit_FunctionDef = visit_FunctionDef
    node_iter.visit(ast.parse(inspect.getsource(target)))
    return decorators

print get_decorators(Foo)

Это должно напечатать что-то вроде этого ...

{'bar': ['baz', 'che']}

или, по крайней мере, так было, когда я тестировал это на Python 2.7.9 очень быстро:)

26 голосов
/ 13 июля 2010

Если вы можете изменить способ вызова декораторов с

class Foo(object):
    @many
    @decorators
    @here
    def bar(self):
        pass

до

class Foo(object):
    @register(many,decos,here)
    def bar(self):
        pass

тогда вы можете зарегистрировать декораторы следующим образом:

def register(*decorators):
    def register_wrapper(func):
        for deco in decorators[::-1]:
            func=deco(func)
        func._decorators=decorators        
        return func
    return register_wrapper

Например:

def many(f):
    def wrapper(*args,**kwds):
        return f(*args,**kwds)
    return wrapper

decos = here = many

class Foo(object):
    @register(many,decos,here)
    def bar(self):
        pass

foo=Foo()

Здесь мы получаем доступ к кортежу декораторов:

print(foo.bar._decorators)
# (<function many at 0xb76d9d14>, <function decos at 0xb76d9d4c>, <function here at 0xb76d9d84>)

Здесь мы печатаем только имена декораторов:

print([d.func_name for d in foo.bar._decorators])
# ['many', 'decos', 'here']
3 голосов
/ 13 июля 2010

Это потому, что декораторы - это "синтаксический сахар".Скажем, у вас есть следующий декоратор:

def MyDecorator(func):
    def transformed(*args):
        print "Calling func " + func.__name__
        func()
    return transformed

И вы применяете его к функции:

@MyDecorator
def thisFunction():
    print "Hello!"

Это эквивалентно:

thisFunction = MyDecorator(thisFunction)

Вы можете встроить«история» в объекте функции, возможно, если вы контролируете декораторы.Могу поспорить, что есть какой-то другой умный способ сделать это (возможно, переопределением назначения), но я, к сожалению, не так уж хорошо разбираюсь в Python.(

2 голосов
/ 13 июля 2010

Как отмечает Фейсал , вы могли бы сами декораторы присоединять метаданные к функции, но, насколько мне известно, это не выполняется автоматически.

1 голос
/ 13 июля 2010

Вы не можете, но еще хуже, есть библиотеки, которые помогут скрыть тот факт, что вы создали функцию для начала.См. Functools или библиотеку декораторов (@decorator, если я смог найти) для получения дополнительной информации.

0 голосов
/ 13 июля 2010

Невозможно сделать вообще, потому что

@foo
def bar ...

точно так же, как

def bar ...
bar = foo (bar)

Вы можете сделать это в определенных особых случаях, например, вероятно, @staticmethod, анализируя функциональные объекты, но не лучше этого.

0 голосов
/ 13 июля 2010

На мой взгляд, это невозможно. Декоратор - это не какой-то атрибут или метаданные метода. Декоратор - это удобный синтаксис для замены функции результатом вызова функции. Подробнее см. http://docs.python.org/whatsnew/2.4.html?highlight=decorators#pep-318-decorators-for-functions-and-methods.

...