Подтвердить что ты не робот

Интроспекция для получения имен декораторов по методу?

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

4b9b3361

Ответ 1

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

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']

Ответ 2

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

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

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 real quick:)

Ответ 3

Вы не можете, по определению. Decorator:

@dec
def foo():
    return 1

- это просто ярлык для:

def foo_internal()
    return 1
foo = dec(foo_internal)

Обратите внимание, что декоратор dec является просто некоторым вызываемым возвращаемым чем-то (функцией, возможно, другим вызываемым объектом). Вы даже не знаете, имеет ли foo какое-либо отношение к декорированному определению, например:

def dec(f):
    def not_foo():
        return 0
    return not_foo

Если вам нужно добавить дополнительную информацию о методах, классах и т.д., например, например. атрибуты в .NET --- просто установите для них некоторые атрибуты.

def foo()
    return 1
foo.decorated = True

Или реализуйте декораторы, которые устанавливают эти атрибуты, если это действительно помогает читать.

def dec(f):
    f.decorated = True
    return f

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

Можно сделать. Но я не уверен, действительно ли это полезно. И это добавляет некоторые накладные расходы для каждого декоратора. Поэтому я не ожидал такого механизма в основном Python, если только он не предоставляет некоторые другие преимущества, такие как упрощение реализации декораторов и т.д.

Ответ 4

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

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

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

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

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

thisFunction = MyDecorator(thisFunction)

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

Ответ 5

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

Ответ 7

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

Ответ 8

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

@foo
def bar ...

точно совпадает с

def bar ...
bar = foo (bar)

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