Доступ к классу, которому принадлежит декорированный метод, из декоратора - PullRequest
4 голосов
/ 16 апреля 2009

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

Пример (из четвертого примера 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

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

Спасибо.

Ответы [ 2 ]

6 голосов
/ 16 апреля 2009

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

__metaclass__

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

По сути, мы конвертируем декоратор 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

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

Есть несколько статей о метаклассах , рекомендованных Дэвидом Мерцем (David Mertz), которые могут вас заинтересовать в этом контексте.

6 голосов
/ 16 апреля 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, если вам не нужно. Я не совсем уверен, что вы пытаетесь сделать, но вещи, связанные с «получить класс владельца», часто бывают выполнимо с использованием метаклассов.)

...