Как сделать одинакову проверку одной и той же переменной класса в разных методах этого класса?

Использую Python 3. Есть класс, в котором есть какая-то переменная, которую обрабатывают несколько методов. Упрощенный пример:

class Foo:
def __init__(self, foobar):
    self.foobar = foobar

def bar_1(self):
    # some operation with self.foobar
    self.foobar = 'Another variable'

def bar_2(self):
    # some operation with self.foobar
    self.foobar = 'Another variable'

def bar_3(self):
    # some operation with self.foobar
    self.foobar = 'Another variable'

Я хочу, чтобы в результате вызова этих методов мне возвращался raise или return о том, что в этом вызове метода переменная self.foobar никак не изменилась.


Есть, по-моему, очевидное и простое решение:

class Foo:
def __init__(self, foobar):
    self.foobar = foobar

def bar_1(self):
    old_foobar = self.foobar
    # some operation with self.foobar
    self.foobar = 'Another variable'
    if old_foobar == self.foobar:
        return 'Variable foobar has not changed'

def bar_2(self):
    old_foobar = self.foobar
    # some operation with self.foobar
    self.foobar = 'Another variable'
    if old_foobar == self.foobar:
        return 'Variable foobar has not changed'

def bar_3(self):
    old_foobar = self.foobar
    # some operation with self.foobar
    self.foobar = 'Another variable'
    if old_foobar == self.foobar:
        return 'Variable foobar has not changed'

Но оно некрасивое, потому что в нём повторяются одни и те же строки. Как мне кажется, лучше сделать это с помощью декоратора, только я не понимаю как мне для декоратора достать текущее значение переменной в момент выполнения какого-либо метода, чтобы потом сравнить исходное с итоговым. Помогите, пожалуйста, найти оптимальное решение


Ответы (2 шт):

Автор решения: splash58

Припишите к первому коду:

@property
def foobar(self):
    return self._foobar

@foobar.setter
def foobar(self, v):
    try:
        if v == self._foobar:
            raise ValueError('Variable foobar has not changed')
    except AttributeError:
        pass
    self._foobar = v

Блок try except нужен, чтоб сработала инициализация атрибута

И тогда следующий код приведет к исключению

f = Foo('Another variable')
f.bar_3()
→ Ссылка
Автор решения: Andy Pavlov

Могу предложить такой вариант, а raise по своему усмотрению встройте.

Через декоратор сохраняем переменные объекта, с которыми потом сравниваем измененные переменные с исходными по ключам в словаре. Дальше при желании можно восстановить исходные значения.

def constants(func):
    def wraps(self_, *args, **kwargs):
        consts = dict(self_.__dict__)
        func(self_, *args, **kwargs)
        if all([v == self_.__dict__[c] for c, v in consts.items()]):
            print('No changes')
        else:
            print('Variables are changed')
    return wraps


class Foo:
    def __init__(self, foobar):
        self.foobar = foobar

    @constants
    def bar_1(self):
        # some operation with self.foobar
        self.foobar = 'Another variable'

    @constants
    def bar_2(self):
        # some operation with self.foobar
        self.foobar = 'Another variable'

    @constants
    def bar_3(self):
        # some operation with self.foobar
        self.foobar = 'Another variable'


f = Foo('123')
print(f.foobar)  # 123
f.bar_1()  # Variables are changed
print(f.foobar)  # Another variable
f.bar_2()  # No changes
print(f.foobar)  # Another variable
f.bar_3()  # No changes
print(f.foobar)  # Another variable
→ Ссылка