Можно ли получить доступ к декоратору функции во время выполнения в Python? [Дубликат]

Чтобы использовать методы и член объекта, вам сначала нужно создать этот объект. Если вы его не создали (переменная, которая должна содержать объект, не инициализируется), но вы пытаетесь использовать его методы или переменные, вы получите эту ошибку.

Иногда вы можете просто забыть инициализировать .

Отредактировано: new не может вернуть значение null, но исключение огня при ошибке. Давно это было на некоторых языках, но не больше. Спасибо @John Saunders за указание на это.

25
задан Tony 12 July 2010 в 21:30
поделиться

8 ответов

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

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']
24
ответ дан unutbu 28 August 2018 в 15:52
поделиться
  • 1
    Это отличное решение. : D Предполагается, что у вас есть доступ к коду, который назначает декораторов, хотя ... – Faisal 12 July 2010 в 22:15
  • 2
    Хорошо, это может сработать, но почему я не могу просто добавить код func._whatever = 'something' в мой существующий декоратор и проверить значение атрибута _whatever при выполнении интроспекции метода? – Tony 12 July 2010 в 22:23
  • 3
    Вы можете, но тогда вам придется загрязнять каждого декоратора, который вы пишете, с кропотливой заботой о том, чтобы оставить свои следы в функции, которую он изменяет. – Faisal 12 July 2010 в 23:01

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

1
ответ дан Achim 28 August 2018 в 15:52
поделиться

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

1
ответ дан Cody Gray 28 August 2018 в 15:52
поделиться

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

@foo
def bar ...

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

def bar ...
bar = foo (bar)

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

0
ответ дан doublep 28 August 2018 в 15:52
поделиться

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

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

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

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

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

thisFunction = MyDecorator(thisFunction)

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

3
ответ дан Faisal 28 August 2018 в 15:52
поделиться

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

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

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, очень быстро:)

23
ответ дан Jaymon 28 August 2018 в 15:52
поделиться
  • 1
    Хорошо, у этого есть проблемы в python 3: (по крайней мере, похоже, и я уверен, что я не лучший человек со знаниями проверки / ast, чтобы прокомментировать этот уровень уверенности); в основном у меня есть код из того, что у вас выше, и inspect.getsource(), кажется, возвращается с пробелами перед def wrapper, что затем дает неожиданную ошибку отступа в вызове ast.parse. – Jmons 19 January 2018 в 12:35
  • 2
    ТАКЖЕ , когда я попытался продемонстрировать это с помощью hte онлайн-скриптов (которые, я думаю, скорее скрипт, чем запуск из файла), я получаю OSError: source code not available, поэтому я подозреваю, что есть экземпляры (возможно, также bin работает), где этот процесс не будет работать. Возможно, это не сработает, если существуют бины только python? – Jmons 19 January 2018 в 12:35
  • 3
    Какая версия python3? Я просто протестировал его в python 2.7.13 и python 3.6.4 (это версии, которые у меня есть на моем компьютере), и оба они отлично работали. Кроме того, я не уверен, что это будет работать повсюду, но он работает везде, где он мне нужен. Я определенно мог видеть, что онлайн-скрипт имеет защиту для модулей, таких как ast и проверяет, и, возможно, другие вещи, такие как открытие файлов, поскольку это, по определению, более содержательная среда. – Jaymon 21 January 2018 в 02:35
  • 4
    Спасибо за проверку: я постараюсь сделать еще один взгляд, но, не имея возможности продемонстрировать с полезным обменом полезным кодом, это усложняет работу. Если я смогу воспроизвести его, я открою его как новый вопрос и пометю вам;) – Jmons 24 January 2018 в 10:49

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

@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
ответ дан Tomek Szpakowicz 28 August 2018 в 15:52
поделиться
  • 1
    «Вы не можете, по определению & quot; похоже на 100% неправильный ответ, учитывая, что Jaymon опубликовал, как это сделать со стандартным пакетом отражения (import inspect) выше. stackoverflow.com/a/31197273/1424877 Я предлагаю удалить этот ответ ... если я не пропущу что-то тонкое. – Quuxplusone 17 April 2017 в 03:49

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

2
ответ дан wheaties 28 August 2018 в 15:52
поделиться
Другие вопросы по тегам:

Похожие вопросы: