Может декоратор Python метода экземпляра получать доступ к классу?

Привет у меня есть что-то примерно как следующее. В основном я должен получить доступ к классу метода экземпляра от декоратора, используемого на метод экземпляра в его определении.

def decorator(view):
    # do something that requires view's class
    print view.im_class
    return view

class ModelA(object):
    @decorator
    def a_method(self):
        # do some stuff
        pass

Код как есть дает

AttributeError: 'function' object has no attribute 'im_class'

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

Спасибо.

99
задан Community 23 May 2017 в 12:34
поделиться

4 ответа

Если вы используете Python 2.6 или более позднюю версию, вы можете использовать декоратор класса, возможно, что-то вроде этого (предупреждение: непроверенный код).

def class_decorator(cls):
   for name, method in cls.__dict__.iteritems():
        if hasattr(method, "use_class"):
            # do something with the method and class
            print name, cls
   return cls

def method_decorator(view):
    # mark the method as something that requires view's class
    view.use_class = True
    return view

@class_decorator
class ModelA(object):
    @method_decorator
    def a_method(self):
        # do some stuff
        pass

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

После создания класса декоратор класса перебирает все методы и делает все необходимое для методов, которые были помечены.

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

63
ответ дан 24 November 2019 в 05:06
поделиться

Как указали Муравьи, вы не можете получить ссылку на класс изнутри класса. Однако, если вам интересно различать разные классы (а не манипулировать фактическим объектом типа класса), вы можете передать строку для каждого класса. Вы также можете передать декоратору любые другие параметры, которые захотите, используя декораторы в стиле класса.

class Decorator(object):
    def __init__(self,decoratee_enclosing_class):
        self.decoratee_enclosing_class = decoratee_enclosing_class
    def __call__(self,original_func):
        def new_function(*args,**kwargs):
            print 'decorating function in ',self.decoratee_enclosing_class
            original_func(*args,**kwargs)
        return new_function


class Bar(object):
    @Decorator('Bar')
    def foo(self):
        print 'in foo'

class Baz(object):
    @Decorator('Baz')
    def foo(self):
        print 'in foo'

print 'before instantiating Bar()'
b = Bar()
print 'calling b.foo()'
b.foo()

Отпечатки:

before instantiating Bar()
calling b.foo()
decorating function in  Bar
in foo

Кроме того, см. Страницу Брюса Эккеля о декораторах.

4
ответ дан 24 November 2019 в 05:06
поделиться

Проблема в том, что при вызове декоратора класс еще не существует. Попробуйте следующее:

def loud_decorator(func):
    print("Now decorating %s" % func)
    def decorated(*args, **kwargs):
        print("Now calling %s with %s,%s" % (func, args, kwargs))
        return func(*args, **kwargs)
    return decorated

class Foo(object):
    class __metaclass__(type):
        def __new__(cls, name, bases, dict_):
            print("Creating class %s%s with attributes %s" % (name, bases, dict_))
            return type.__new__(cls, name, bases, dict_)

    @loud_decorator
    def hello(self, msg):
        print("Hello %s" % msg)

Foo().hello()

Эта программа выведет:

Now decorating <function hello at 0xb74d35dc>
Creating class Foo(<type 'object'>,) with attributes {'__module__': '__main__', '__metaclass__': <class '__main__.__metaclass__'>, 'hello': <function decorated at 0xb74d356c>}
Now calling <function hello at 0xb74d35dc> with (<__main__.Foo object at 0xb74ea1ac>, 'World'),{}
Hello World

Как видите, вам придется придумать другой способ делать то, что вы хотите.

3
ответ дан 24 November 2019 в 05:06
поделиться

У вас будет доступ к классу объекта, для которого вызывается метод, в декорированном методе, который должен вернуть ваш декоратор. Примерно так:

def decorator(method):
    # do something that requires view's class
    def decorated(self, *args, **kwargs):
        print 'My class is %s' % self.__class__
        method(self, *args, **kwargs)
    return decorated

Используя ваш класс ModelA, вот что он делает:

>>> obj = ModelA()
>>> obj.a_method()
My class is <class '__main__.ModelA'>
0
ответ дан 24 November 2019 в 05:06
поделиться
Другие вопросы по тегам:

Похожие вопросы: