【问题标题】:Creating a list of mutable items repeated N times创建重复 N 次的可变项列表
【发布时间】:2021-04-15 22:50:06
【问题描述】:

我有一个包含一组x 可变项的列表。我想创建另一个列表,其中 x 可变项集重复 n 次。但是,这些项目必须是对唯一对象的引用,而不是对原始对象的简单引用。

例如,让a = [[1],[2],[3]]。假设我希望里面的数字重复n=3 次,即[[0],[2],[3],[1],[2],[3],[1],[2],[3]]

我可以使用a * 3 轻松做到这一点。问题是如果我更改a[0][0] = 0,那么我将得到a == [[0],[2],[3],[0],[2],[3],[0],[2],[3]],这是不可取的,因为我只想更改第一个元素。有没有其他方法可以做到这一点?

【问题讨论】:

  • 只是一些迂腐的:1 不是可变的,尽管 [1] 是可变的。当您执行lst[idx] = val 时发生的变化是lst[idx]绑定 已更改,绑定引用的原始值保持不变。一旦你理解了底层的绑定/不变性概念,你的 Python 技能就会达到一个新的水平。
  • @paxdiablo。好的,我编辑了我的问题以供将来参考。

标签: python list-comprehension multiplication replicate


【解决方案1】:

itertools 有一个称为repeat 的方法。只需将其与chain 配对即可。

from itertools import repeat, chain

a = [1,2,3]

b = list(chain(*repeat(a, 3)))

>>> b
[1,2,3,1,2,3,1,2,3]

【讨论】:

  • 这不是给[[1, 2, 3], [1, 2, 3], [1, 2, 3]]吗?
【解决方案2】:
a = [1,2,3]
a = a*3  
a[0] =0
print(a)

【讨论】:

  • 如果你得到前两个2s(id(a[1])id(a[4]))的id,你会发现它们是相同的对象。
【解决方案3】:

非常简单,您需要复制a,而不是重复对原始可变对象的引用。如果您只需要一个级别的副本,您可以这样做,注意它是一个 副本(请参阅第二个输出)。

a = [[1, 1], [2], [3, 3, 3]]
b = a[:] * 3
b[0] = "change"
print(b)
b[2][1] = "deep"
print(b)

输出:

['change', [2], [3, 3, 3], [1, 1], [2], [3, 3, 3], [1, 1], [2], [3, 3, 3]]
['change', [2], [3, 'deep', 3], [1, 1], [2], [3, 'deep', 3], [1, 1], [2], [3, 'deep', 3]]

看看如何只为第一级提供新的参考。当您尝试更改更深的元素时,您会看到嵌套列表仍然是同一个对象。要解决那个问题,请为b 的每个元素使用深拷贝:

import copy

a = [[1, 1], [2], [3, 3, 3]]
b = [copy.deepcopy(a) for _ in range(3)]
b[0] = "change"
print(b)
b[2][1] = "deep"
print(b)

输出:

['change', [[1, 1], [2], [3, 3, 3]], [[1, 1], [2], [3, 3, 3]]]
['change', [[1, 1], [2], [3, 3, 3]], [[1, 1], 'deep', [3, 3, 3]]]

【讨论】:

    【解决方案4】:

    只要做:

    n = [sub_a.copy() for sub_a in a] * 3
    

    你需要copy()来避免这个问题。

    a = [[1],[2],[3]]
    
    n = [sub_a.copy() for sub_a in a] * 3
    
    a[0] = [-1]
    a[2][0] = -1
    
    print (n)
    

    输出:

    [[1], [2], [3], [1], [2], [3], [1], [2], [3]]
    

    【讨论】:

    • 这只会创建三个对同一个底层副本的引用,实际上没有任何改变。
    • 谢谢。我以为copy() 会根据docs 创建一个浅拷贝。你的意思是deepcopy()
    • 好吧,我编辑的确切术语可能有误。 copy() 足以做你想做的事,因为我认为 deepcopy() 也应该如此。
    • @Synthase 好吧,您的代码当然“有效”,因为a 是一个整数列表。整数是不可变的。 OP 专门询问一组可变对象。用列表列表试试这个。
    • @Paul,据我所知,OP 在示例中采用整数。很抱歉使用 OP 的示例。
    【解决方案5】:

    要获取可变对象的新实例,可以将对象类型的 copy() 方法映射到相乘列表:

    a  = [{1},{2},{3}]
    b  = [*map(set.copy,a*3)]
    
    a[0].add(7)
    b[0].add(8)
    b[3].add(9)
    print(a) # [{1, 7}, {2}, {3}]
    print(b) # [{8, 1}, {2}, {3}, {1, 9}, {2}, {3}, {1}, {2}, {3}]
    

    如果您不知道项目的类型,或者它们有不同的类型,您可以在列表推导中进行:

    b = [ i.copy() for i in a*3 ]
    

    如果不是所有的项目都是可变的,你可以这样做:

    b = [type(i)(i) for i in a*3]
    

    如果有些可以是 None ...

    b = [i if i is None else type(i)(i) for i in a*3]
    

    您还可以使用涵盖所有这些情况的 deepcopy,包括包含嵌套对象的对象...

    from copy import deepcopy
    b  = [*map(deepcopy,a*3)]
    

    【讨论】:

      【解决方案6】:

      我刚刚发现我可以通过以下方式做到这一点:

      from copy import deepcopy
      a = [[1],[2],[3]]
      n = 3
      b = [deepcopy(val) for _ in range(n) for val in a]
      

      现在如果我设置b[0][0] = 0,我会得到b == [[0],[2],[3],[1],[2],[3],[1],[2],[3]]

      【讨论】:

      • 看来b[0] = 1 已将其设置为零:-)
      • @Synthase:像“低于”或“高于”这样的术语在订单可能发生变化的网站上是个坏主意。
      • 修正了我的答案。
      • @paxdiablo 对我来说已经太晚了啊哈哈
      猜你喜欢
      • 2011-03-28
      • 2020-09-28
      • 2013-05-28
      • 2016-10-29
      • 2016-01-07
      • 1970-01-01
      • 2019-02-16
      • 2020-02-20
      相关资源
      最近更新 更多