2009-04-15 2 views
4

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

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

Так я ищу код, чтобы напечатать вместо # здесь я хочу ...

Спасибо.

ответ

6

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

__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 

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

Существует очень рекомендуемый Дэвид Мерц, который может показаться интересным в этом контексте: articles on metaclasses.

+0

Я не использовал ваш код напрямую, потому что моя проблема была гораздо более конкретной, чем я говорил вам. Но использование * name * при пересечении * dct * немного нечетно, так как * name * обычно используется в конструкции * type *. Спасибо, я думаю, что все равно согласиться на ваш ответ, потому что это мне очень помогло. – Gra

+0

О, это ошибка, исправлена. –

6

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

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

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() декоратор построен, прежде чем функция присваивается класс владельца.

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

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

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

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

Смежные вопросы