【问题标题】:Shallow or deep copy in a list comprehension列表理解中的浅拷贝或深拷贝
【发布时间】:2014-05-25 10:56:43
【问题描述】:

如果您在 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) 是否会导致原始列表的浅拷贝或深拷贝?这件事与我有关,如果原始列表包含对象,哪些属性可以在代码中更改以跟随 New_List 创建(ThirdList)。 New_list 被发送到一个函数,该函数将更改属性。问题是您是否尝试将原始列表重用于具有不同理解的相同功能(比如说 (members>4)。

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

其实:

print New_List[3].visited

给出真值。

【问题讨论】:

  • 你为什么不试试(除了列表中的数字)? (剧透:这是一个浅拷贝。为什么它应该是深拷贝?您在列表理解中明确使用原始members。)顺便说一句,您的New_Listset...
  • 切片类似于深拷贝。这已经足够了,因为 a 没有嵌套列表。我不确定理解是否会产生类似于切片的深拷贝输出。
  • @MartijnPieters:是的,抱歉,我纠正了错字。现在是一个列表:-)
  • @Ivaylo 它看起来像一个深拷贝,因为在您的两个示例中,您都使用不可变类型。此外,您正在为列表元素 (list2[1] = 'x') 分配新值,而不是 更改 列表元素。尝试相同的操作,例如列表列表和 list2[1].append(stuff)
  • @Ivaylo:这完全取决于您的列表包含的内容。如果您在该列表中有复杂的可变对象也需要复制而不是共享,则使用copy.deepcopy() 或扩展您的自定义对象以添加.copy() 方法,该方法创建仅复制无法共享的新对象。跨度>

标签: python list python-2.7 list-comprehension


【解决方案1】:

另一个对我有用的想法是在类中实现一个 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,这对我来说真的是一次学习经历。

【讨论】:

    【解决方案2】:

    您正在创建一个浅层的过滤副本。

    您的循环不会创建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 中包含的同一字典的引用。更改该字典会反映在 copysample 中,但附加到 copy 不会更改原始列表。

    至于您更新的循环代码,您的示例会生成一个仍然共享对象的New_List 列表,而New_List[3].visited 实际上是True

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

    因为它仍然是在索引 7 处的 Original 中找到的相同对象:

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

    这仍然是在ThirdList 的索引 2 中找到的相同对象:

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

    【讨论】:

    • 我承认这是一个很有启发性的例子。
    • 我的对象是一种:class CustomID (object): def __init__(self, *args): self.ID = '' self.visited = False self.isnoise = False
    • 什么是自定义 .copy() 以仅对 self.visited 进行深度复制?
    • @Ivaylo:那些是具有不可变属性的简单对象;添加一个自定义 .copy() 方法来创建一个新的 CustomID 对象,并复制这 3 个属性就可以了。
    • @Ivaylo:它确实改变了Original 中的值。您确实需要考虑到您过滤了原始列表,而不是 所有 对象都是共享的。
    猜你喜欢
    • 1970-01-01
    • 2012-04-12
    • 2015-01-13
    • 2011-09-05
    • 1970-01-01
    • 1970-01-01
    • 2019-01-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多