在字符串属性中反映 Python 列表变化

Reflect Python list change in string attribute

我正在寻找一种优雅(高效)的方式来实现以下内容:

我有一个 class 将值列表存储为 字符串 (带有分隔符,例如:“ - ”)。我使用 属性(getter 和 setter)将此字符串转换为 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)来自动反映 list.

中的变化
c.value_list.append("four")
assert c.values == "one - two - three - four"

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

目前,我实现了自己的 list class 继承 collections.MutableSequence 的回调系统。 有更好的方法吗?

编辑:我目前的解决方案

我将 list 与 "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 以实现此处理程序以反映更改。

class的新版本是:

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"

我会通过重载您想要支持的某些运算符来解决这个问题,例如 += operator by defining __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"

有关运算符重载的更多信息,请参阅 docs

也许我过于简单化了您的问题,因为您过于简化了您的第一个示例,但我同意一些评论者的观点,并建议您改变存储它的方式。 存储列表,按需生成字符串。

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'