列表推导中的浅拷贝或深拷贝
如果你在Python中有一个列表(我们叫它原始列表),比如:
class CustomID (object):
def __init__(self, *args):
self.ID = ''
self.manymore = float()
self.visited = False
self.isnoise = False
IDlist = ['1','2','3','4','5','6','7','8','9','10']
Original = list()
for IDs in IDlist:
NewObject = CustomID()
NewObject.ID = IDs
Original.append(NewObject)
然后你用列表推导式创建一个新列表,并且有一个函数要在这个新列表的子列表上使用:
def Func(InputList=list()):
for objects in InputList:
objects.visited = True
return InputList
New_List = [member for member in Original if (int(member.ID)>5)]
ThirdList = Func(New_List)
那么这个新列表(New_List)是对原始列表的浅拷贝还是深拷贝呢?这个问题对我来说很重要,因为如果原始列表里面有对象,而这些对象的属性在创建新列表(ThirdList)后可能会改变。新列表会被传递给一个函数,这个函数会改变这些属性。我的问题是,如果你想用原始列表去重复调用同一个函数,但使用不同的列表推导式(比如说成员数大于4),会发生什么。
New_List = [member for member in Original if (int(member.ID)>4)]
实际上:
print New_List[3].visited
结果是True。
2 个回答
另一个对我有效的想法是,在这个类里实现一个叫做flagClear的方法。
class CustomID (object):
def __init__(self, *args):
self.ID = ''
self.manymore = float()
self.visited = False
self.isnoise = False
def flagClear(self):
self.visited = False
return self
然后,每次我创建一个新列表的时候,就简单地调用这个方法:
New_List = [member.flagClear() for member in Original if (int(member.ID)>4)]
如果我在CustomID中唯一修改的就是.visited这个标记,那这样做是可以的。当然,这并不是完美的。如果有人需要一个完整的解决方案,Martijn Pieters的建议会更好(实现.copy()方法):
import copy
class CustomID (object):
def __init__(self, *args):
self.ID = ''
self.manymore = float()
self.visited = False
self.isnoise = False
def CustomCopy(self):
return copy.deepcopy(self)
New_List = [member.CustomCopy() for member in Original if (int(member.ID)>4)]
谢谢你,Martijn,这对我来说真是一次学习的经历。
你正在创建一个浅层的、过滤过的副本。
你的循环并没有真正复制member
,而是直接引用了它们。
其实你也不需要复制,因为你原始列表里的所有对象都是不可变的整数。而且,CPython会对小整数进行优化,复制这些整数只会导致使用完全相同的对象。
为了说明这一点,试着创建一个包含可变对象的列表副本。我这里用了一个字典:
>>> sample = [{'foo': 'bar'}]
>>> copy = [s for s in sample]
>>> copy[0]['spam'] = 'eggs'
>>> copy.append({'another': 'dictionary'})
>>> sample
[{'foo': 'bar', 'spam': 'eggs'}]
这个copy
列表是一个新的列表对象,它包含了对sample
中同一个字典的引用。修改这个字典会在copy
和sample
中都反映出来,但在copy
中添加内容并不会改变原始列表。
至于你更新后的循环代码,你的示例生成了一个New_List
列表,它仍然共享对象,而New_List[3].visited
实际上是True
:
>>> New_List[3].ID
'8'
>>> New_List[3].visited
True
因为它仍然是Original
中索引为7的那个对象:
>>> New_List[3] is Original[7]
True
而且这个对象在ThirdList
中索引为2的位置也能找到:
>>> ThirdList[2] is New_List[3]
True