【问题标题】:In class object, how to auto update attributes when you change only one entry of a list?在类对象中,当您仅更改列表的一个条目时如何自动更新属性?
【发布时间】:2016-09-29 15:56:25
【问题描述】:

我已经看到了一个非常相似的问题的答案:

In class object, how to auto update attributes?

我把代码贴在这里:

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]

在这段代码中,当我使用以下方法更新列表时:

>>> c.list = [1, 2, 3, 4]

c.listsquare 将相应更新:

>>> c.listsquare
[1, 4, 9, 16]

但是当我尝试时:

>>> c.list[0] = 5
>>> c.list
[5, 2, 3, 4]

Listsquares 未更新:

>>> c.listsquare
[1, 4, 9, 16]

当我只更改列表中的一项时,如何使 listsquare 自动更新?

【问题讨论】:

    标签: python class attributes


    【解决方案1】:

    你可以做到这一点的一种方法是拥有一个私有助手_List 类,它几乎与内置的list 类完全一样,但也有一个owner 属性。每次为 _List 实例的元素之一赋值时,它就可以修改其 ownerlistsquare 属性以使其保持最新。由于它仅供SomeClass 使用,因此可以嵌套在其中以提供更多封装。

    class SomeClass(object):
        class _List(list):
            def __init__(self, owner, *args, **kwargs):
                super(SomeClass._List, self).__init__(*args, **kwargs)
                self.owner = owner
    
            def __setitem__(self, index, value):
                super(SomeClass._List, self).__setitem__(index, value)
                self.owner.listsquare[index] = int(value**2)
    
        def __init__(self, n):
            self.list = SomeClass._List(self, 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)
    print(c.list)        # --> [0, 1, 2, 3, 4]
    print(c.listsquare)  # --> [0, 1, 4, 9, 16]
    c.list[0] = 5
    print(c.list)        # --> [5, 1, 2, 3, 4]
    print(c.listsquare)  # --> [25, 1, 4, 9, 16]
    

    【讨论】:

      【解决方案2】:

      首先,我建议您不要通过访问单个 list.setter 来更改两个不同的属性。这样做的原因:

      >>> c.list[0] = 5
      >>> c.list
      [5, 2, 3, 4]
      >>> c.listsquare
      [1, 4, 9, 16]
      

      不行,你正在访问 c.list.__setitem__ 方法。

      c.list[0] = 5 
      is equal to
      c.list.__setitem__(0, 5)
      or
      list.__setitem__(c.list, 0, 5)
      and as such, the list.__setitem__ method isn't the one you've implemented in your class.
      

      但是,如果你真的想这样做,你应该重新考虑基于 self._list 创建方形列表。

      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
      
          @property
          def listsquare(self):
              return [n ** 2 for n in self.list]
      
          @listsquare.setter
          def listsquare(self, val):
              self.list = [int(pow(x, 0.5)) for x in val]
      

      【讨论】:

        猜你喜欢
        • 2013-02-01
        • 2019-01-20
        • 2020-02-21
        • 2021-10-21
        • 2017-04-24
        • 2018-03-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多