Отразить изменение списка Python в строковом атрибуте

Я ищу элегантный (эффективный) способ реализовать следующее:

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

class C(object):
    def __init__(self, values):
        """
        Construct C with a string representing a list of values.

        :type values: str
        :param values: List of values
        """
        self.values = values

    @property
    def value_list(self):
        """ Values as list """
        return self.values.split(" - ")

    @value_list.setter
    def value_list(self, seq):
        self.values = " - ".join(seq)

Получение / установка свойства в порядке:

c = C("one - two")
assert c.value_list == ["one", "two"]

c.value_list = ["one", "two", "three"]
assert c.values == "one - two - three"

Но я ищу что-то (может быть другой вид списка), чтобы автоматически отражать изменения в list,

c.value_list.append("four")
assert c.values == "one - two - three - four"

Traceback (most recent call last):
...
AssertionError

В настоящее время я реализую свой собственный list наследование классов collections.MutableSequence с системой обратного вызова. Есть ли лучший способ сделать это?

РЕДАКТИРОВАТЬ: мое текущее решение

Я использую список с обработчиком on_change, например:

class MyList(collections.MutableSequence):
    """ A ``list`` class with a "on_change" handler. """

    def __init__(self, *args):
        self._list = list(*args)

    def on_change(self, seq):
        pass

    def __getitem__(self, index):
        return self._list.__getitem__(index)

    def __len__(self):
        return self._list.__len__()

    def __setitem__(self, index, value):
        self._list.__setitem__(index, value)
        self.on_change(self._list)

    def __delitem__(self, index):
        self._list.__delitem__(index)
        self.on_change(self._list)

    def insert(self, index, value):
        self._list.insert(index, value)
        self.on_change(self._list)

Тогда мне нужно изменить мой C класс для реализации этого обработчика, чтобы отразить изменения.

Новая версия класса:

class C(object):
    def __init__(self, values):
        """
        Construct C with a string representing a list of values.

        :type values: str
        :param values: List of values
        """
        self.values = values

    def _reflect_changes(self, seq):
        self.values = " - ".join(seq)

    @property
    def value_list(self):
        """ Values as list """
        my_list = MyList(self.values.split(" - "))
        my_list.on_change = self._reflect_changes
        return my_list

    @value_list.setter
    def value_list(self, seq):
        self.values = " - ".join(seq)

Таким образом, любое изменение в списке отражается в values атрибут:

c = C("one - two")

c.value_list.append("three")
assert c.values == "one - two - three"

c.value_list += ["four"]
assert c.values == "one - two - three - four"

2 ответа

Решение

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

In [1]: class C(object):
   ...:     def __init__(self, values):
   ...:         self.values = values.split(' - ')
   ...:     @property
   ...:     def value_str(self):
   ...:         return ' - '.join(self.values)
   ...:

In [2]: c = C('one - two - three')

In [3]: c.values
Out[3]: ['one', 'two', 'three']

In [4]: c.values.append('four')

In [5]: c.value_str
Out[5]: 'one - two - three - four'

Я хотел бы подойти к этому, перегружая определенные операторы, которые вы хотите поддерживать, например оператор +=, определяя__iadd__, Тогда вы сможете сделать что-то вроде:

class C(object):
    def __init__(self, values):
        self._values = values

    def __iadd__(self, value):
        self._values += " - " + str(value)
        return self

obj = C("one - two")
obj += "three"

print(obj.values) # "one - two - three"

Смотрите документы для получения дополнительной информации о перегрузке операторов.

Другие вопросы по тегам