python:如何拥有一个属性和一个setter函数来检测值发生的所有更改

python:如何拥有一个属性和一个setter函数来检测值发生的所有更改,python,properties,Python,Properties,我有两个属性来保存列表。每当此列表中的任何项目发生更改时,我希望另一个列表能够自我更新。这包括语句obj.myProp[3]=5。现在,该语句调用getter函数来获取整个列表,从列表中获取第三项,并将其设置为5。myProp列表已更改,但第二个列表从未更新 class Grid(object): def __init__(self,width=0,height=0): # Make self._rows a multi dimensional array

我有两个属性来保存列表。每当此列表中的任何项目发生更改时,我希望另一个列表能够自我更新。这包括语句
obj.myProp[3]=5
。现在,该语句调用getter函数来获取整个列表,从列表中获取第三项,并将其设置为5。
myProp
列表已更改,但第二个列表从未更新

class Grid(object):

    def __init__(self,width=0,height=0):

        # Make self._rows a multi dimensional array
        # with it's size width * height
        self._rows=[[None] * height for i in xrange(width)]
        # Make `self._columns` a multi dimensional array
        # with it's size height * width
        self._columns=[[None] * width for i in xrange(height)]

    @property
    def rows(self):
        # Getting the rows of the array
        return self._rows

    @rows.setter
    def rows(self, value):
        # When the rows are changed, the columns are updated
        self._rows=value
        self._columns=self._flip(value)

    @property
    def columns(self):
        # Getting the columns of the array
        return self._columns

    @columns.setter
    def columns(self, value):
        # When the columns are changed, the rows are updated
        self._columns = value
        self._rows = self._flip(value)

    @staticmethod
    def _flip(args):
        # This flips the array
        ans=[[None] * len(args) for i in xrange(len(args[0]))]
        for x in range(len(args)):
            for y in range(len(args[0])):
                ans[y][x] = args[x][y]
        return ans
运行示例:

>>> foo=grid(3,2)
>>> foo.rows
[[None, None], [None, None], [None, None]]
>>> foo.columns
[[None, None, None], [None, None, None]]
>>> foo.rows=[[1,2,3],[10,20,30]]
>>> foo.rows
[[1, 2, 3], [10, 20, 30]]
>>> foo.columns
[[1, 10], [2, 20], [3, 30]]
>>> foo.rows[0][0]=3
>>> foo.rows
[[3, 2, 3], [10, 20, 30]]
>>> foo.columns
[[1, 10], [2, 20], [3, 30]]
如果您查看最后三行,这就是实际问题发生的地方。我将子列表的第一项设置为3,但
foo.columns
从不更新自身,将3放入其列表中

简言之,如何使一个变量始终更新另一个变量,即使它的子项正在更改


我使用的是Python2.7

如果您只返回
列表,那么您将永远无法控制更改项目时会发生什么

一种可能是不提供直接获取/设置列表的属性,而是为位于x/y位置的项提供setter/getter

一个好的版本是让
\uuuuu setitem\uuuuuuuuuuuuuuu
/
\uuuuuuuu getitem\uuuuuuuuuuuuuu
并让它们接受一个元组,这样您就可以使用
foo[x,y]
foo[x,y]=bar
访问元素


另一种方法是在列表周围返回一个包装器,用于检测元素何时被更改,但是您还必须对每个嵌套列表执行此操作。

您的问题是您没有在有问题的行上设置
foo.rows
——您得到了它,然后修改了它的一个成员。这不会解雇二传手。对于您提议的API,您需要返回一个包含getter和setter的列表

您最好不要使用rows和columns属性来设置条目,并添加一个
\uuu getitem\uuu
方法,如下所示:

class Grid(object):

    def __init__(self, width=0, height=0):
        self._data = [None] * width * height;
        self.width = width
        self.height = height

    def __getitem__(self, pos):
        if type(pos) != tuple or len(pos) != 2:
            raise IndexError('Index must be a tuple of length 2')
        x, y = pos
        if 0 <= x < self.width and 0 <= y < self.height:
            return self._data[x + self.width * y]
        else:
            raise IndexError('Grid index out of range')

    def __setitem__(self, pos, value):
        if type(pos) != tuple or len(pos) != 2:
            raise IndexError('Index must be a tuple of length 2')
        x, y = pos
        if 0 <= x < self.width and 0 <= y < self.height:
            self._data[x + self.width * y] = value
        else:
            raise IndexError('Grid index out of range')

    @property
    def columns(self):
        return [
            [self[x, y] for x in xrange(self.width)]
            for y in xrange(self.height)
        ]

    @property
    def rows(self):
        return [
            [self[x, y] for y in xrange(self.height)]
            for x in xrange(self.width)
        ]

我建议你考虑使用。特别是,该方法给出了轴变换的基础矩阵的“视图”,这意味着如果您更改一个,那么另一个也会更改

如果这不符合您的需要,那么我所能看到的使您想要的API工作的唯一方法就是定义“行”和“列”方法,以返回自定义的“视图对象”,这些对象实际上并不存储数据,而是返回到网格对象本身中的一些私有(共享)数据。这些可以是类似列表的,但不支持某些操作(例如,追加或清除)

foo[0, 0] = 3