Доступ к классу, владеющему декорированным методом, из декоратора

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

Пример (из четвертого примера PEP 318):

def returns(rtype):
    def check_returns(f):
        def new_f(*args, **kwds):
            result = f(*args, **kwds)
            assert isinstance(result, rtype), \
                   "return value %r does not match %s" % (result,rtype)
            return result
        new_f.func_name = f.func_name
        # here I want to reach the class owning the decorated method f,
        # it should give me the class A
        return new_f
    return check_returns

class A(object):
    @returns(int)
    def compute(self, value):
        return value * 3

Итак, я ищу код для ввода вместо # здесь, который я хочу...

Спасибо.


person Gra    schedule 15.04.2009    source источник


Ответы (2)


Как сказал bobince, вы не можете получить доступ к окружающему классу, потому что во время вызова декоратора класс еще не существует. Если вам нужен доступ к полному словарю класса и баз, вам следует рассмотреть метакласс:

__metaclass__

Эта переменная может быть любой вызываемой, принимающей аргументы для имени, базы и словаря. При создании класса вместо встроенного type() используется callable.

По сути, мы преобразуем декоратор returns во что-то, что просто сообщает метаклассу, что нужно поколдовать над созданием класса:

class CheckedReturnType(object):
    def __init__(self, meth, rtype):
        self.meth = meth
        self.rtype = rtype

def returns(rtype):
    def _inner(f):
        return CheckedReturnType(f, rtype)
    return _inner

class BaseInspector(type):
    def __new__(mcs, name, bases, dct):
        for obj_name, obj in dct.iteritems():
            if isinstance(obj, CheckedReturnType):
                # do your wrapping & checking here, base classes are in bases
                # reassign to dct
        return type.__new__(mcs, name, bases, dct)

class A(object):
    __metaclass__ = BaseInspector
    @returns(int)
    def compute(self, value):
        return value * 3

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

Есть несколько статей о метаклассах, написанных настоятельно рекомендуемым Дэвидом Мерцем. , что может показаться вам интересным в этом контексте.

person Torsten Marek    schedule 15.04.2009
comment
Я не использовал ваш код напрямую, потому что моя проблема была гораздо более конкретной, чем я вам сказал. Но использование name при обходе dct немного странно, поскольку name обычно используется в конструкции type. Спасибо, я думаю, что все равно можно принять ваш ответ, потому что он мне очень помог. - person Gra; 16.04.2009

здесь я хочу добраться до класса, владеющего декорированным методом f

Вы не можете, потому что в момент оформления ни один класс не владеет методом f.

class A(object):
    @returns(int)
    def compute(self, value):
        return value * 3

Это то же самое, что сказать:

class A(object):
    pass

@returns(int)
def compute(self, value):
    return value*3

A.compute= compute

Ясно, что декоратор returns() создается до того, как функция будет назначена классу-владельцу.

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

>>> A.compute.im_class
<class '__main__.A'>

Таким образом, вы можете прочитать f.im_class внутри «new_f», который выполняется после присваивания, но не в самом декораторе.

(И даже тогда немного некрасиво полагаться на детали реализации CPython, если вам это не нужно. Я не совсем уверен, что вы пытаетесь сделать, но вещи, связанные с «получением класса владельца», часто выполнимы с помощью метаклассов. .)

person bobince    schedule 15.04.2009