Можно ли получить значение в дочернем классе, сгенерированное декоратором, определенным в базовом классе? - PullRequest
0 голосов
/ 30 мая 2011

У меня есть метод дочернего класса, который я хочу условно замкнуть. Я пытаюсь сделать что-то вроде этого, за исключением того, что я хочу поместить логику проверки в базовый класс.

class BaseClass(object):
    def getvalue(self):
        return True
    def validate(self):
        validated = self.getvalue()
        return validated

class ExtendedClass1(BaseClass):
    def do_some_work(self):
        validated = self.validate()
        if not validated:
            print "Not validated."
            return
        print "Things are validated if the method got this far.", validated

class ExtendedClass2(BaseClass):
    def do_some_work(self):
        validated = self.validate()
        if not validated:
            print "Not validated."
            return
        print "Things are validated if the method got this far.", validated

class ExtendedClass3(BaseClass):
    def do_some_work(self):
        print "This one doesn't require validation."

work1 = ExtendedClass1()
work1.do_some_work()

work2 = ExtendedClass2()
work2.do_some_work()

work3 = ExtendedClass3()
work3.do_some_work()

Следуя этому примеру, я смог преобразовать часть повторяющегося кода в шаблон декоратора.

class BaseClass(object):

    def validate(input_function):
        def wrapper(*args,**kwargs):
            validated = True
            if not validated:
                print "Not validated."
                return
            input_function(*args, **kwargs)
        return wrapper

    validate = staticmethod(validate)

class ExtendedClass1(BaseClass):
    @BaseClass.validate
    def do_some_work(self):
        print "Things are validated if the method got this far."

class ExtendedClass2(BaseClass):
    @BaseClass.validate
    def do_some_work(self):
        print "Things are validated if the method got this far."

class ExtendedClass3(BaseClass):
    def do_some_work(self):
        print "This one doesn't require validation."

work1 = ExtendedClass1()
work1.do_some_work()

work2 = ExtendedClass2()
work2.do_some_work()

work3 = ExtendedClass3()
work3.do_some_work()

Однако мне нужно вызвать метод базового класса в декораторе для выполнения проверки и получить значение (проверено) в дочернем классе. Следуя этому примеру здесь , я модифицировал декоратор в попытке разрешить ему вызывать self.getvalue (). На данный момент он не выдает ошибку, но он также не работает, потому что self.getvalue () не возвращает True. Это начинает казаться большим количеством проблем, чем оно того стоит, но теперь мне любопытно, возможно ли это.

class BaseClass(object):

    def getvalue(self):
        return True

    def validate(self):
        def wrap(input_function):
            def wrapper(*args,**kwargs):
                validated = self.getvalue()
                if not validated:
                    print "Not validated."
                    return
                input_function(*args, **kwargs)
            return wrapper
        return wrap

    validate = staticmethod(validate)

class ExtendedClass1(BaseClass):
    @BaseClass.validate
    def do_some_work(self):
        print "Things are validated if the method got this far."#, validated

class ExtendedClass2(BaseClass):
    @BaseClass.validate
    def do_some_work(self):
        print "Things are validated if the method got this far."#, validated

class ExtendedClass3(BaseClass):
    def do_some_work(self):
        print "This one doesn't require validation."

work1 = ExtendedClass1()
work1.do_some_work()

work2 = ExtendedClass2()
work2.do_some_work()

work3 = ExtendedClass3()
work3.do_some_work()

Можно ли установить атрибут с помощью декоратора, а затем получить его позже?

                ...
                self.validated = True
                if not self.validated:
                    print "Not validated."
                    return
                ...
print work1.validated
                ...

AttributeError: 'ExtendedClass1' object has no attribute 'validated'

По сути, я хочу включить это:

class ExtendedClass1(BaseClass):
    def do_some_work(self):
        validated = self.validate()
        if not validated:
            print "Not validated."
            return
        print "Things are validated if the method got this far.", validated

В это:

class ExtendedClass1(BaseClass):
    @BaseClass.validate
    def do_some_work(self):
        print "Things are validated if the method got this far.", validated

Используя предложение Заура Насибова, этот пример удовлетворяет моему варианту использования. Мне все еще интересно знать, может ли @validate быть реализован как метод, а не как отдельная функция, но это выполняет свою работу.

class BaseClass(object):
    def getvalue(self):
        return True

def validate(func):
    def wrapped(self, *args, **kwargs):
        validated = self.getvalue()
        self.validated = validated
        if not validated:
            print "Not validated."
            return
        func(self, *args, **kwargs)
    return wrapped

class ExtendedClass1(BaseClass):
    @validate
    def do_some_work(self,input):
        print "Things are validated if the method got this far.", self.validated, input

class ExtendedClass2(BaseClass):
    @validate
    def do_some_work(self):
        print "Things are validated if the method got this far.", self.validated

class ExtendedClass3(BaseClass):
    def do_some_work(self):
        print "This one doesn't require validation."#, self.validated

work1 = ExtendedClass1()
work1.do_some_work(input="some text")

work2 = ExtendedClass2()
work2.do_some_work()

work3 = ExtendedClass3()
work3.do_some_work()

Ответы [ 2 ]

1 голос
/ 30 мая 2011

Как насчет вызова декорированного метода, только если он был проверен?Вы можете передать возвращаемое значение метода validate, если хотите:

class BaseClass(object):
    def getvalue(self):
        return True

    def validate(input_function):
        def wrapper(self, *args, **kwargs):
            self.validated = self.getvalue()
            if not self.validated:
                print "Not validated."
                return
            input_function(self, validated=self.validated, *args, **kwargs)
        return wrapper

    validate = staticmethod(validate)

class ExtendedClass1(BaseClass):
    @BaseClass.validate
    def do_some_work(self, validated=None):
        print "Things are validated if the method got this far.", validated

class ExtendedClass2(BaseClass):
    @BaseClass.validate
    def do_some_work(self, validated=None):
        print "Things are validated if the method got this far.", validated

class ExtendedClass3(BaseClass):
    def do_some_work(self):
        print "This one doesn't require validation."

work1 = ExtendedClass1()
work1.do_some_work()

work2 = ExtendedClass2()
work2.do_some_work()

work3 = ExtendedClass3()
work3.do_some_work()

Ключ здесь добавляет self к функции wrapper.Что происходит, так это то, что ваши декорированные функции не привязываются к экземпляру (и становятся методами), но функция, возвращаемая декоратором (wrapper в приведенном выше примере), вместо этого получает привязку.Таким образом, эта функция получит параметр self (экземпляр), переданный при вызове!Важно помнить, что @decorator просто вызывает decorator, передавая функцию, которую вы декорируете, а затем заменяет функцию, которую вы декорируете, тем, что вернул декоратор.В вашем примере это wrapper, и для класса нет никакой разницы между этой функцией и исходной функцией до ее декорирования.

В вышеприведенном примере я объявил self явно.Если бы мы этого не сделали, мы могли бы просто взять его из args:

def validate(input_function):
    def wrapper(*args, **kwargs):
        print "args[0] is now the instance (conventionally called 'self')", args[0]
        self = args[0]
        self.validated = self.getvalue()
        if not self.validated:
            print "Not validated."
            return
        input_function(validated=self.validated, *args, **kwargs)

Также обратите внимание, что мы передаем дополнительный аргумент ключевого слова в упакованный метод с именем validated.Это совершенно необязательно, вы можете просто удалить части validated=self.validated и validated=None из примера.

1 голос
/ 30 мая 2011

@ tponthieux, вы можете установить атрибут вызываемой функции (метода) и затем извлечь его:

Простой пример ( обновлено ):

def validate(func):    
    def wrapped(self, *args, **kwargs):
        self.valid = True
        func(self, *args, **kwargs)
    return wrapped

class TestClass(object):
    @validate
    def do_some_work(self):
        print "some work done"

tc = TestClass()
tc.do_some_work()
print tc.valid
...