简体   繁体   English

Python:为什么从对象检索列表后,列表会发生变化

[英]Python: why does my list change after I've retrieved it from an object

Simple question, I've scaled down a problem I'm having where a list which I've retrieve from an object is changing when I append more data to the object. 一个简单的问题,当我向对象添加更多数据时,我缩小了一个问题,即从对象中检索到的列表正在更改。 Not to the list. 不在清单上。

Can anyone help my understand the behavior of 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

OutPut: 输出:

['Hello']
['Hello', 'World']

When you code 当你编码

`list = obj.getLog()`

(ignoring -- just for a second -- what a terrible idea it is to use identifiers that shadow builtins!!!) you're saying: "make name list refer to exactly the same object that obj.getLog() returns" -- which as we know from the code for class a is obj.log . (忽略(仅一秒钟)使用阴影内置的标识符是多么可怕的想法!),您要说的是:“使名称list指向obj.getLog()返回的完全相同的对象”- -从class a的代码中我们知道class aobj.log So of course since now you have one list object with two names, when you alter that object through either name, all alterations will be fully visible from both names, of course -- remember, there is just one object, you're just using multiple names for it! 当然如此,因为现在你有两个名字,当你改变通过任一名称对象一个列表对象,所有的改变将是这两个名字完全可见的,当然-记住, 只有 一个对象,你仅仅使用多个名字吧! You never asked for a copy, so of course Python made no copies. 您从未要求提供副本,因此Python当然不会提供副本。

When you want a copy, instead of the original, ask for one ! 当您想要一份副本而不是原始副本时, 索取一份 When you know the type you require (here, a list), the best way is to call the type, ie: 当您知道所需的类型(此处为列表)时,最好的方法是调用该类型,即:

mylist = list(obj.getLog())

This of course becomes impossible if you choose to trample all over the builtins with your identifiers -- -- which is a good part of why such identifier choice is a BAD idea (I can't stress that enough: it's hard to think of any worse style choice, to use in your Python coding, than such naming). 如果您选择使用标识符遍历所有内置函数,那么这当然是不可能的--这就是为什么这样的标识符选择是一个糟糕的主意的一个很好的部分(我对此并不十分强调:很难想到任何比这种命名更糟糕的样式选择(在Python编码中使用)。 So, I've renamed the identifier to mylist (and of course you need to rename it in the two print statements). 因此,我已将标识符重命名为mylist (当然,您需要在两个print语句中将其重命名)。

You could use highly unreadable or slower approaches to make up for the wanton destruction of the normal functionality of built-in identifier list , of course -- eg: 可以使用高度不可读或较慢的方法来弥补对内置标识符list的正常功能的肆意破坏,例如:

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

or 要么

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

or 要么

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

but BY FAR the simplest, cleanest, most recommended approach is to NOT name your identifiers the same as Python built-ins (it's also a nice idea to avoid naming them just like modules in the standard Python library, for similar though a bit weaker reasons). 但是通过FAR ,最简单,最干净,最推荐的方法是不要将标识符的名称与Python内置名称相同(避免将它们命名为标准Python库中的模块一样,这也是一个好主意,尽管有一些弱点,但类似的原因) )。

The only place you create a new list is in the constructor, with the statement: 创建新列表的唯一位置是在构造函数中,其声明如下:

self.log = []

Later, when you do: 稍后,当您这样做时:

list = obj.getLog()

just puts a reference to the same list in a new variable (note, don't use list as a variable name, since it shadows the type). 只是在新变量中放置了对相同列表的引用(请注意,请勿将list用作变量名,因为它会遮盖类型)。 It does not create or clone a list in any way. 它不会以任何方式创建或克隆列表。 If you want to clone it, do: 如果要克隆它,请执行以下操作:

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

You can also use a tuple (read-only sequence), if that's appropriate: 如果合适的话,您还可以使用元组(只读序列):

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

This may help minimize confusion about which should be modified. 这可以帮助最大程度地减少关于应修改哪个方面的困惑。

Look at this method: 看一下这个方法:

def getLog(self):
        return self.log

You've returned a reference to self.log and assigned it to list. 您已返回对self.log的引用,并将其分配给list。 Now they both point to the same list on the heap. 现在它们都指向堆上的相同列表。 When you change self.log, list points to the same location in memory. 更改self.log时,列表指向内存中的相同位置。

You'd have to make a clone and assign that to list for the two to be independent. 您必须制作一个克隆并将其分配给列表,以使两者独立。

Objects are passed around in Python by reference - Python doesn't make copies for you. 对象通过引用在Python中传递-Python不会为您创建副本。 The line 线

return self.log

returns a reference to the list object used internally by obj . 返回对obj内部使用的list对象的引用。 This means that after the line 这意味着行后

list = obj.getLog()

your list variable refers to the same object as obj.log . 您的list变量与obj.log 同一对象。 To acquire a copy instead, use Python's slice syntax: 要获取副本,请使用Python的slice语法:

list = obj.getLog()[:]

What this boils down to is that languages like python and ruby try to make it easy for people to refer to arbitrary, complex objects - associative containers of lists of associative containers and such like. 归结为,诸如python和ruby之类的语言试图使人们更容易地引用任意,复杂的对象-关联容器列表中的关联容器等。 These get too complex to index into repeatedly (and it would be slow), so scripters want to be able to say 这些变得太复杂而无法重复索引(这会很慢),因此脚本编写者希望能够说

galleries = world['asia']['japan'][3]['Tokyo']['Galleries']

then make queries against or updates to the galleries in Tokyo. 然后对东京的画廊进行查询或更新 That is sometimes convenient (when doing updates), sometimes - as in your case where you think you've made a private copy of some data and want to massage one without affecting the other - very confusing and inconvenient. 有时很方便(在进行更新时),有时-就像您认为自己已经对某些数据进行了私有复制并希望在不影响其他数据的情况下进行处理那样-造成极大的混乱和不便。 But, most scripting languages will adopt this same strategy because to make full independent copies of the data each time would be hideously slow for the common case of just wanting a "handle" through which to inspect some part of the larger data structure. 但是,大多数脚本语言将采用相同的策略,因为对于仅希望通过“句柄”检查较大数据结构的某些部分的常见情况,每次制作数据的完全独立副本将非常缓慢。 It's ugly and inconsistent with the way they handle basic types like numbers and textual strings. 它们处理数字和文本字符串等基本类型的方式很丑陋且不一致。 As others have said, you need to use: 正如其他人所说,您需要使用:

new_list = original_list[:] # slice off a complete copy of a list
new_dictionary = original_dictionary.copy()  # for dictionaries

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 使用Macports选择其他python后,为什么我的系统python会运行? - Why does my system python get run when I've selected a different python using Macports? Python-为什么当我检查返回的对象不是NoneType时,此方法为什么返回NoneType? - Python - why does this method return NoneType when I've checked that the object I'm returning is not NoneType? 为什么在我取消链接后,我重新向量化的 sys.stdout.write() 方法会被调用? - Why does my revectored sys.stdout.write() method get called after I've unlinked it? Python:为什么使用 b = list(a) 复制列表后 id 会发生变化 - Python: Why does the id change after copying a list with b = list(a) 如何在Python GAE中从检索到的Google云存储对象访问文件 - How can I access my file from a retrieved google cloud storage object in Python GAE 为什么我的子例程在调用另一个子例程后仍继续执行我的所有if语句 - Why does my sub-routine continue to carry out all of my if statements after I've called another sub-routine Python:为什么我的列表在我没有实际更改的情况下会更改? - Python: why does my list change when I'm not actually changing it? 为什么我在 Python 中安装了 selenium 之后它仍然无法工作? - Why is selenium not working even after I've installed it in Python? 为什么我的* args结果在Python 3中以空列表开头? - Why does my result from *args begin with an empty list in Python 3? 从表中获取时间戳格式后,为什么会更改时间戳格式? - Why does the timestamp format change after I fetch it from table?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM