Функция декоратора, которая вернет себя? - PullRequest
0 голосов
/ 14 февраля 2019

Мне нужно следующий класс, который создаст объект с цепочечными методами, производными от переменных класса.Поскольку этот код довольно повторяющийся, моя задача состоит в том, чтобы создать декоратор, который можно применять поверх методов a, b и c.Проблема, с которой я сталкиваюсь, заключается в том, что я не могу найти способ создать оболочку, которая будет возвращать экземпляр (self).Есть ли лучший способ построить это?

class Test:

    def __init__(self, a, b, c):
        self._a = a
        self._b = b
        self._c = c

        self.call_chain = []


    def a(self, truth):
        def func():
            return self._a == truth
        self.call_chain.append(func)
        return self


    def b(self, truth):
        def func():
            return self._b == truth
        self.call_chain.append(func)
        return self


    def c(self, val):
        def func():
            return self._c == val
        self.call_chain.append(func)
        return self


    def evaluate(self):
        try:
            for f in self.call_chain:
                if f() == False:
                    raise ValueError('False encountered')
        except ValueError:
            self.call_chain.clear()
            return False 
        self.call_chain.clear()
        return True

Он работает так:

c = Test(True, False, 13)
c.a(True).b(False).c(13).evaluate()

1 Ответ

0 голосов
/ 14 февраля 2019

Хитрость заключается в хранении аргументов функции как части цепочки вызовов.Самый простой способ - использовать functools.partial объекты.

from functools import wraps, partial

def chain(func):
    @wraps(func)
    def wrapper(self, *args, **kwargs):
        suspended = partial(func, self, *args, **kwargs)
        self.call_chain.append(suspended)
        return self
    return wrapper

class Test:
    def __init__(self, a, b, c):
        self.call_chain = []
        self._a = a
        self._b = b
        self._c = c
    @chain
    def a(self, val):
        return self._a == val
    @chain
    def b(self, val):
        return self._b == val
    @chain
    def c(self, val):
        return self._c == val
    def evaluate(self):
        try:
            for f in self.call_chain:
                if f() == False:
                    raise ValueError('False encountered')
        except ValueError:
            self.call_chain.clear()
            return False 
        self.call_chain.clear()
        return True

c = Test(True, False, 13)
c.a(True).b(False).c(13).evaluate()  # True
c.a(True).b(False).c(11).evaluate()  # False
...