Python: декорировать __call__ и обычную функцию одним и тем же декоратором? - PullRequest
3 голосов
/ 12 марта 2012

Возможно ли в Python 2.6-2.7 использовать один и тот же декоратор для следующей задачи:

class ComplextCallableObject(object):
    @wrap_me
    def __call__(self, a1, a2):
        pass

@wrap_me
def simple_function(a1, a2):
    pass

И ComplextCallableObject.__call__, и simple_function имеют одинаковые аргументы, но __call__ также имеет self за первый аргументВ декораторе wrap_me мне нужен доступ к функции, заключаемой в аргументы.

Ответы [ 3 ]

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

К сожалению, во время определения (в данном случае это блок классов), код не может сказать, как будет использоваться функция, за исключением соглашения об именовании. Немного изменив свой пример:

class ComplextCallableObject(object):
    @wrap_me
    def __call__(self, a1, a2):
        pass #...

@wrap_me
def simple_function(tgt, a1, a2):
    pass

ComplextCallableObject.anInstanceMethod = simple_function
ComplextCallableObject.anClassMethod = classmethod(simple_function)
ComplextCallableObject.aStaticMethod = staticmethod(simple_function)

В этом случае simple_function реализует функцию, принимающую цель и два параметра, метод экземпляра, принимающий два параметра, метод класса, принимающий два параметра, и статический метод, принимающий цель и два параметра. Но эти виды использования не связаны до тех пор, пока функция не будет определена. И staticmethod, и classmethod возвращают разные типы объектов, так что вы можете различить их, если это необходимо.

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

def wrap_me(fn):
    names = fn.func_code.co_varnames
    if names and names[0]=='self':
        print 'looks like an instance method'
    else: print 'looks like a function'
    return fn
0 голосов
/ 13 марта 2012

Это может быть довольно глупо, но может сработать и для простейшего случая:

In [1]: def wrap_me(func):
   ...:     def wrapped(*args):
   ...:         print 'arg1 is', args[-2]
   ...:         print 'arg2 is', args[-1]
   ...:         func(*args)
   ...:     return wrapped
   ...:
In [2]: class ComplexCallableObject(object):
   ...:     @wrap_me
   ...:     def __call__(self, a1, a2):
   ...:         print 'class object called'
   ...:
In [3]: @wrap_me
   ...: def simple_function(a1, a2):
   ...:     print 'function called'
   ...:
In [4]: simple_function('A', 'B')
arg1 is A
arg2 is B
function called
In [5]: o = ComplexCallableObject()
In [6]: o('A', 'B')
arg1 is A
arg2 is B
class object called
0 голосов
/ 12 марта 2012
def wrap_me(*args):
    a1, a2 = args if len(args) == 2 else args[1:]
    ...

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

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

...