Python:为什么我的列表在从obj中检索出来后会发生变化

2024-10-04 07:29:54 发布

您现在位置:Python中文网/ 问答频道 /正文

简单的问题,我缩小了一个问题,当我向对象追加更多数据时,我从一个对象检索到的列表会发生变化。不在名单上。在

有人能帮助我理解python的行为吗?在

class a():
    def __init__(self):
        self.log = []
    def clearLog(self):
        del self.log[:]
    def appendLog(self, info):
        self.log.append(str(info))
    def getLog(self):
        return self.log

if __name__ == '__main__':
    obj = a()
    obj.appendLog("Hello")

    # get an instance as of this moment....
    list = obj.getLog()
    print list

    obj.appendLog("World")

    # print list, BUT we want the instance that was obtained
    # before the new appendage.   
    print list

输出:

^{pr2}$

Tags: the数据对象instanceselfinfologobj
3条回答

创建新列表的唯一位置是在构造函数中,其中包含以下语句:

self.log = []

稍后,当您执行以下操作时:

^{pr2}$

只需将对同一列表的引用放入一个新变量中(注意,不要使用list) 作为变量名,因为它隐藏了类型)。它不会以任何方式创建或克隆列表。如果要克隆它,请执行以下操作:

def getLog(self):
    return list(self.log)

如果合适,还可以使用元组(只读序列):

def getLog(self):
    return tuple(self.log)

这可能有助于最大限度地减少关于哪些内容应该修改的混淆。在

看看这个方法:

def getLog(self):
        return self.log

您已返回对的引用self.log日志并将其分配到列表中。现在它们都指向堆上的同一个列表。当你改变self.log日志,列表指向内存中的同一位置。在

你必须制作一个克隆并将其分配给list,这样两者才能独立。在

当你编码的时候

`list = obj.getLog()`

(忽略——只是一秒钟——使用隐藏的标识符是多么可怕的想法!!!)你的意思是:“make name list引用obj.getLog()返回的完全相同的对象”—正如我们从class a的代码中知道的那样,obj.log。当然,既然现在你有一个列表对象有两个名称,当你通过其中一个名称来改变这个对象,所有的改变都会从这两个名称中完全可见,当然,记住,就是只是一个对象,你只是在为它使用多个名称!你从来没有要求拷贝,所以Python当然没有拷贝。在

当你想要一份副本而不是原件时,要一份!当您知道您需要的类型(这里是一个列表)时,最好的方法是调用该类型,即:

^{pr2}$

当然,如果你选择用你的标识符践踏所有的内置函数——这就是为什么这样的标识符选择是一个坏的想法的一个很好的部分(我再强调也不为过:很难想出比这样的命名更差的样式选择,在你的Python编码中使用)。因此,我已经将标识符重命名为mylist(当然,您需要在两个print语句中重命名它)。在

当然,可以使用高度不可读或较慢的方法来弥补对内置标识符list正常功能的肆意破坏——例如:

import copy
list = copy.copy(obj.getLog())   # somewhat slower

或者

list = obj.getLog()[:]           # traditional, but ECCH

或者

temp = obj.getLog()
list = type(temp)(temp)          # abstruse

但是,到目前为止,最简单、最干净、最受推荐的方法是不要将标识符命名为Python内置的标识符(这也是一个不错的主意,避免像标准Python库中的模块一样命名它们,原因类似,尽管有点弱)。在

相关问题 更多 >