[英]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 a
是obj.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.