Я пытаюсь выяснить, как получить имена всех декораторов в методе. Я уже могу получить имя метода и docstring, но не могу понять, как получить список декораторов.
Интроспекция для получения имен декораторов по методу?
Ответ 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
, если бы я мог ее найти).
Ответ 6
Это невозможно по-моему. Декоратор - это не какой-то атрибут или метаданные метода. Декоратор - удобный синтаксис для замены функции результатом вызова функции. Подробнее см. http://docs.python.org/whatsnew/2.4.html?highlight=decorators#pep-318-decorators-for-functions-and-methods.
Ответ 7
Как заметки Фейсала, вы могли бы сами декораторы приложить метаданные к функции, но, насколько мне известно, это не делается автоматически.
Ответ 8
Это невозможно сделать в общем виде, потому что
@foo
def bar ...
точно совпадает с
def bar ...
bar = foo (bar)
Вы можете сделать это в некоторых особых случаях, например, @staticmethod
путем анализа объектов функций, но не лучше этого.