В объекте класса, как автоматически обновлять атрибуты?

У меня есть класс, который имеет несколько атрибутов, которые связаны, например:

class SomeClass:
    def __init__(self, n=0):
        self.list = range(n)
        self.listsquare = [ x**2 for x in self.list ]

Если я делаю объект нормально, что бы не проблема,

a = SomeClass(10)

Я получу 2 списка, a.list и a.listsquare.

Теперь, если я хочу сначала создать пустой объект и присвоить ему один атрибут, я хочу, чтобы другие атрибуты были автоматически обновлены, например, если я делаю

b = SomeClass()
b.list = range(5,10)

Я хочу, чтобы b.listsquare автоматически обновлялся, а также наоборот (назначить b.listsquare и автоматическое обновление b.list). Это возможно? Является ли класс правильным выбором для этого?


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

Я хотел бы получить класс Foo с 3 атрибутами length, list и listsquare такими, что:

  • Если я делаю a = Foo(3), я получаю a.length = 3, a.list = [0, 1, 2], a.listsquare = [0, 1, 4].
  • Если я делаю b = Foo().list = [5, 6], я получаю b.length = 2, b.listsquare = [25, 36].
  • Если я делаю c = Foo().listsquare = [4, 9], я получаю c.length = 2, c.list = [2, 3].

Ответ 1

если обновление одного свойства из-за обновления другого свойства - это то, что вы ищете (вместо пересчета значения свойства downstream для доступа) используйте средства настройки свойств:

class SomeClass(object):
    def __init__(self, n):
        self.list = range(0, n)

    @property
    def list(self):
        return self._list
    @list.setter
    def list(self, val):
        self._list = val
        self._listsquare = [x**2 for x in self._list ]

    @property
    def listsquare(self):
        return self._listsquare
    @listsquare.setter
    def listsquare(self, val):
        self.list = [int(pow(x, 0.5)) for x in val]

>>> c = SomeClass(5)
>>> c.listsquare
[0, 1, 4, 9, 16]
>>> c.list
[0, 1, 2, 3, 4]
>>> c.list = range(0,6)
>>> c.list
[0, 1, 2, 3, 4, 5]
>>> c.listsquare
[0, 1, 4, 9, 16, 25]
>>> c.listsquare = [x**2 for x in range(0,10)]
>>> c.list
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Ответ 2

Совершенно верно. Но вместо этого используйте свойство.

class SomeClass(object):
  def __init__(self, n=5):
    self.mylist = range(n)

  @property
  def listsquare(self):
    return [ x**2 for x in self.mylist ]

a = SomeClass()
a.mylist = [4, 5, 8]
print a.listsquare

Кэширование значения свойства остается в качестве упражнения для читателя.

Ответ 3

Вы также можете использовать методы setter, например:

class SomeClass:
    def __init__(self, n=5):
        self.set_list(range(n))

    def set_list(self, n):
        self.list = n
        self.listsquare = [ x**2 for x in self.list ]

b = SomeClass()
b.set_list(range(5,10))

Ответ 4

Решение Ignacio @property отлично, но оно пересчитывает список каждый раз, когда вы ссылаетесь на список списков - это может стать дорогостоящим. Решение Mathew замечательно, но теперь у вас есть функциональные вызовы. Вы можете комбинировать их с функцией "свойство". Здесь я определяю getter и setter для my_list (я просто не мог назвать его "list"!), Который генерирует listquare:

class SomeClass(object):

    def __init__(self, n=5):
        self.my_list = range(n)

    def get_my_list(self):
        return self._my_list

    def set_my_list(self, val):
        self._my_list = val
        # generate listsquare when my_list is updated
        self.my_listsquare = [x**2 for x in self._my_list]

    # now my_list can be used as a variable
    my_list = property(get_my_list, set_my_list, None, 'this list is squared')

x = SomeClass(3)
print x.my_list, x.my_listsquare
x.my_list = range(10)
print x.my_list, x.my_listsquare

Выводится:

[0, 1, 2] [0, 1, 4]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]