Shallow or deep copy in a list comprehension

爷,独闯天下 提交于 2021-01-28 03:04:30

问题


If you have a list (Original) in python like:

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)

and if you do a comprehension for a new list and a function to use over the comprehension sublist:

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)

Is this (New_List) resulting in a shallow or deep copy of the original list? The matter is relevant for me, if the original list contains objects, which attributes can change in the code to follow New_List creation (ThirdList). New_list is send to a function, which will change the attributes. The question is if you try to reuse the original list for the same function with different comprehension (lets say (members>4).

New_List = [member for member in Original if (int(member.ID)>4)]

Actually:

print New_List[3].visited

gives True.


回答1:


You are creating a shallow, filtered copy.

Your loop doesn't create copies of member, it references them directly.

Not that you would need to create copies, all objects in your original list are immutable integers. Moreover, CPython interns small integers, creating copies would only result in the exact same objects being used for these.

To illustrate, try to create a copy of a list containing mutable objects instead. Here I used a dictionary:

>>> sample = [{'foo': 'bar'}]
>>> copy = [s for s in sample]
>>> copy[0]['spam'] = 'eggs'
>>> copy.append({'another': 'dictionary'})
>>> sample
[{'foo': 'bar', 'spam': 'eggs'}]

The copy list is a new list object containing a reference to the same dictionary contained in sample. Altering that dictionary is reflected in both copy and sample, but appending to copy doesn't alter the original list.

As for your updated loop code, your sample produces a New_List list that still shares objects, and New_List[3].visited is in fact True:

>>> New_List[3].ID
'8'
>>> New_List[3].visited
True

because it is still the same object found in Original at index 7:

>>> New_List[3] is Original[7]
True

which is the same object still found in ThirdList at index 2:

>>> ThirdList[2] is New_List[3]
True



回答2:


Another idea, which worked for me is to implement a flagClear method in the class

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

And then, every time I construct a new list, simply to use the method:

New_List = [member.flagClear() for member in Original if (int(member.ID)>4)]

If the only thing I modify in the CustomID is the .visited flag, than this works. Obviously, it will not be perfect. If someone needs a complete solution, the suggestion of Martijn Pieters will work best (implementing .copy() method):

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)]

Thank you Martijn, this was really a learning experience for me.



来源:https://stackoverflow.com/questions/23009999/shallow-or-deep-copy-in-a-list-comprehension

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!