class 变量的不可理解的行为

Incomprehensible behaviour of class variables

我不得不说我真的没有问题,就像 "everything works fine",但我很难理解为什么。

我正在 python/urwid 中编写脚本。在我的脚本中,我有一个 class ItemWidget,其 self._w 等于 3 个小部件的 urwid.Pile,每个小部件都是 urwid.Text 行。此外,class ItemWidget 有一个 属性 self.visibility,这是一个包含三个布尔值的列表,还有一个方法 self.rebuild()。初始化函数是

def __init__ (self, content,vis):
    self.content = content
    self.visibility = vis
    self.rebuild()
    self.__super.__init__(self._w)    

rebuild() 方法根据 self.visibility() 中的值重建 self._w。

在脚本的 main() 函数中我有一个变量

globalvisibility = [1,1,1]

我创建了一个(大约 1000 个)ItemWidget 实例列表:

for content in abstracts:                               # (*)
    items.append(ItemWidget(content,globalvisibility))  #

其实我用的是变量而不是

for content in abstracts.items():                        
    items.append(ItemWidget(content,[1,1,1]))            

只是因为后面我会实现保存全局可见性到一个文件。但是我认为除了这个初始化之外它不会对任何东西有用。

无论如何,在脚本中我经常使用 ItemWidget 的实例并通过例如

更改其可见性
item.visibility[2] = 0    # (**)

和类似的。到目前为止,一切正常,符合预期。

这是我的问题。为什么在通过 (*) 创建 ItemWidget 实例后,将变量全局可见性更改为 [0,1,1] 会影响所有这些实例?

我不得不说我 "discovered" 它是无意中发现的,它似乎非常有用(我认为要更改所有 ItemWidget 实例的可见性属性,我必须遍历所有实例),但是 -对我来说 - 非常令人惊讶。特别是考虑到操作 (**) 仅影响单个实例。

非常感谢一些解释。

编辑:整个脚本在这里: https://www.dropbox.com/s/a0a4a0asyi5lyxw/tescik.py?dl=0 和示例数据库,如果 itemwidget 是: https://www.dropbox.com/s/zuwbvggznst85ru/arxiv-2013-05-23.db?dl=0

为了 运行 您需要修改第 58 行以指向数据库。要查看行为,请滚动到某些项目并按 enter - 摘要将仅在这些项目上消失,然后按 "show abstracts" - 这将重置所有项目的行为。

Why after creating the instances of ItemWidget via (*), changing the variable globalvisibility to, say, [0,1,1] affects all these instances?

因为您在创建所有这些实例时都通过了 globalvisibility。所有实例都在其 vis 属性中存储对同一列表的引用。

如果您想避免这种情况,请在 __init__ 方法中或在创建实例时复制列表:

def __init__ (self, content,vis):
    self.content = content
    self.visibility = vis[:]    # here
    # etc.

或者:

for content in abstracts:
    items.append(ItemWidget(content, globalvisibility[:]))  

您在此处做出的确切选择取决于您是否需要当前的行为。如果是这样,请将其从 __init__() 中删除,并在制作实例时进行复制。