Как я могу увидеть, является ли метод декоратором? - PullRequest
2 голосов
/ 21 марта 2012

Можно ли проверить функцию / метод, чтобы увидеть, можно ли ее использовать в качестве декоратора?В чем заключается обычный способ декораторов обернуть другие функции и вернуть вызываемый?В частности, я хочу проверить сторонний код.

Ответы [ 5 ]

2 голосов
/ 21 марта 2012

Применяя предполагаемый декоратор, перехватывая исключения и затем проверяя, содержит ли результат метод __call__, вы могли бы сделать предположение относительно того, является ли данный вызываемый объект декоратором или нет. Но это будет только предположение, а не гарантия.

Кроме того, я не верю, что то, что вы хотите, будет вообще возможным из-за динамически типизированной природы языка Python и особой обработки встроенных функций в интерпретаторе CPython. Невозможно программно определить, будет ли вызываемый объект принимать другой вызываемый в качестве аргумента или какой тип будет иметь его возвращаемое значение. Кроме того, в CPython для функций, реализованных в C, вы даже не можете проверить вызываемый объект, чтобы увидеть, сколько аргументов он принимает.

Слово «декоратор» может означать разные вещи. Один из способов определить это - декоратор - это любой вызываемый объект, который принимает один (вызываемый) аргумент и возвращает вызываемый элемент.

Обратите внимание, что я даже не использовал слово "функция" в этом определении; это было бы на самом деле неправильно. Действительно, некоторые часто используемые декораторы имеют странные свойства:

  • Встроенные декораторы classmethod и staticmethod возвращают дескрипторные объекты, а не функции.
  • Начиная с языковой версии 2.6 вы можете украшать классы, а не только функции и методы.
  • Любой класс, содержащий метод __init__(self, somecallable) и метод __call__(self, *args, **kwargs), может использоваться в качестве декоратора.
2 голосов
/ 21 марта 2012

Поскольку в Python нет стандартизированного декоратора, нет реального способа определить, является ли функция декоратором, если вы не знаете что-то о декораторе, который ищете.

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

def decorator(func):
    return g

@decorator
def f()
    pass

def g():
    pass

В приведенном выше примере во время выполнения f и g будут идентичны, и невозможно разделить их на две части.

1 голос
/ 21 марта 2012

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

@foo
def bar(...):

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

def bar(...):
   ...
bar = foo(bar)

Естественно, поскольку foo может вернуть что угодно , у вас нетспособ проверить, была ли функция оформлена или нет.* * * * * * * * * * *

* * * * * * * *1015* Если вам дают какой-то код Python, и вы хотите найти все вещи, которые являются декораторами, вы можетесделайте это, проанализировав код в абстрактном синтаксическом дереве, а затем обойдя дерево в поисках декорированных функций.Вот пример, хранящий .id s декораторов.Очевидно, что вы можете хранить ast объекты, если хотите.

>>> class DecoratorFinder(ast.NodeVisitor):
...     def __init__(self, *args, **kwargs):
...         super(DecoratorFinder, self).__init__(*args, **kwargs)
...         self.decorators = set()
...     
...     def visit_FunctionDef(self, node):
...         self.decorators.update(dec.id for dec in node.decorator_list)
...         self.generic_visit(node)
... 
>>> finder = DecoratorFinder()
>>> x = ast.parse("""
... @dec
... def foo():
...     pass
... """)
>>> finder.visit(x)
>>> finder.decorators
set(['dec'])
0 голосов
/ 21 марта 2012

Нет, это невозможно. Может быть, вместо проверки, является ли f декоратором, вы должны подумать, зачем вам это проверять?

Если вы ожидаете какого-то определенного декоратора, вы можете проверить это напрямую, если вы хотите, чтобы какое-то определенное поведение / методы / атрибуты можно было проверить,

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

Вот такая наивная проверка:

def decorator1(func):
    def _wrapper(*args, **kwargs):
        print "before"
        func(*args, **kwargs)
        print "after"

    return _wrapper

def dummy_func(): pass

out_func = decorator1(dummy_func)

if callable(out_func) and dummy_func != out_func:
    print "aha decorated!"
0 голосов
/ 21 марта 2012

Я никогда не делал ничего подобного, но в целом Python полагается на "типизацию утки" в подобных ситуациях.Таким образом, вы можете просто попытаться украсить фиктивную функцию и посмотреть, возвращается ли вызываемый объект.

...