I'm searching an elegant (an efficient) way to implement the following:
I have a class storing a list of values as a string (with a separator, eg.: " - "). I use a property (getter and setter) to convert this string into a 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)
Getting / Setting the property is OK:
c = C("one - two")
assert c.value_list == ["one", "two"]
c.value_list = ["one", "two", "three"]
assert c.values == "one - two - three"
But I'm looking for something (may be another kind of list) to automatically reflect the changes in the list
.
c.value_list.append("four")
assert c.values == "one - two - three - four"
Traceback (most recent call last):
...
AssertionError
Currently, I implement my own list
class inheriting collections.MutableSequence
with a callback system.
Is there a better way to do that?
EDIT: my current solution
I use a list with a "on_change" handler, like this:
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)
Then I need to modify my C
class to implement this handler to reflect the changes.
The new version of the class is:
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)
That way, any change in the list in reflected in the values
attribute:
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"
Maybe I'm oversimplifying your problem because you've oversimplified your first example, but I'm going to agree with some of the commenters and propose that you change around the way you store this. Store the list, produce the string on demand.
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'