2016-06-06 5 views
1

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

У меня есть класс, хранящий список значений в качестве строки (с сепаратором, например, .: "-"). Я использую свойство (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.

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

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

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

EDIT: мое текущее решение

Я использую список с "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" 
+4

Почему бы не сохранить список и не сгенерировать строку, а наоборот? – BrenBarn

+0

Как насчет 'c.value_list + = [" four "]'? –

+0

@BrenBam: На самом деле я решительно решу проблему. Я получаю ** string ** значения из проанализированных данных (файл). Некоторые значения представляют собой скалярные/не изменяемые данные (здесь нет проблем), но некоторые представляют собой коллекции. Я выбираю наиболее распространенный: ** список **. –

ответ

1

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

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' 
1

Я хотел бы подойти к этому при перегрузке некоторых операторов вы хочу поддержать, например, += 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 для получения дополнительной информации о перегрузке операторов.

+0

Конечно, вы можете добавить операторов. Но это решение не является общим. Как насчет нескольких атрибутов, таких как '' _values''? –