简体   繁体   English

如何在Python中复制包含嵌套元组的元组?

[英]How can I make a copy of a tuple containing nested tuples in Python?

I'm working on a question that requires me to define a function copy_tree that takes an argument tree (tuple that may contain tuples) and return a copy of the tree (ie. stored in a different memory location). 我正在研究一个需要我定义一个函数copy_tree的问题,该函数接受一个参数tree (可能包含元组的元组)并返回该树的副本(即存储在不同的内存位置)。

My current code is: 我当前的代码是:

def copy_tree(tree):
    new_tree = ()
    for i in tree:
        new_i = (i,) + ()
        new_tree = new_tree + new_i
    return new_tree

However, this does not work for the nested tuples, as the tuples inside are not "copied" but rather referred to. 但是,这不适用于嵌套的元组,因为内部的元组不是“复制”的,而是被引用的。

eg if I run 例如,如果我跑步

a = ((3, 2), 1, (4,), 5)
b = copy_tree(a)
print(a[0] is b[0])

the output has to be False . 输出必须为False

How do I make the tuples copies? 如何制作元组副本?

Edit: I am not allowed to use the deepcopy module. 编辑:我不允许使用deepcopy模块。

Here is a recursive solution that deep copies (nested) tuples, leaves other objects unchanged, and doesn't use the copy module: 这是一个递归解决方案,它可以深复制(嵌套)元组,使其他对象保持不变,并且不使用copy模块:

def copy_tree(tree):
    if isinstance(tree, tuple):
        return tuple(map(copy_tree, tree))
        # or, maybe more readable
        # return tuple(copy_tree(x) for x in tree)
    return tree

Recursion is definitely the most elegant approach if you do not know nesting levels in advance. 如果您事先不知道嵌套级别,那么绝对肯定是最优雅的方法。

Simply passing an existing tuple to the tuple constructor will not create a new copy of the constructor. 仅将现有元组传递给tuple构造函数将不会创建该构造函数的新副本。 Instead, you should pass an equivalent list to the tuple constructor to make a copy of a tuple: 相反,您应该将等效列表传递给tuple构造函数以复制元组:

def copy_tuple(t):
    output = []
    for i in t:
        if isinstance(i, tuple):
            output.append(copy_tuple(i))
        else:
            output.append(i)
    return tuple(output)

so that: 以便:

a = ((3, 2), 1, (4,), 5)
b = copy_tuple(a)
print(a)
print(b)
print(a[0] is b[0])

would output: 将输出:

((3, 2), (4,), 5)
((3, 2), (4,), 5)
False

Your code is missing the recursive step - you only copy the top-level tuple. 您的代码缺少递归步骤-您仅复制顶级元组。

def copy_tree(tree):
  new_tree = ()
  for i in tree:
    # i is not copied
    new_i = (i,) + ()
    new_tree = new_tree + new_i
  return new_tree

Adding recursion produces copies of every layer of the tree. 添加递归会生成树的一层的副本。 Note that you may copy only tuples: 请注意,您可以复制的元组:

def copy_tree(tree):
  new_tree = ()
  for i in tree:
    # recursively copy i *if it is a tuple*
    if isinstance(i, tuple):
      new_i = (copy_tree(i),)
    else:
      new_i = (i,)
    new_tree = new_tree + new_i
  return new_tree

Recursion fixes the result, but the approach you have taken is inefficient - there are many transient tuples created and thrown away. 递归可修复结果,但是您采用的方法效率低下-创建并丢弃了许多临时元组。 Every time a tuple is "extended" via + , the old one is thrown away and a new one created. 每次通过+ “扩展”一个元组时,都会丢弃旧的元组,并创建一个新的元组。

A first step is to delay creation of the tuple until all children are converted: 第一步是延迟创建元组,直到所有子级都被转换为止:

def copy_tree(tree):
    children = []
    for child in tree:
        # we *always* preserve a child, so ternary if expresses this better
        child = copy_tree(child) if isinstance(child, tuple) else child
        children.append(child)
    # create a new tuple including all children
    return tuple(children)

Since that list only exists to be turned into a tuple, we can get rid of that as well: a generator expression allows to feed the converted children directly to the tuple constructor. 由于该列表仅存在,可以变成一个元组,因此我们也可以摆脱它:生成器表达式允许将转换后的子级直接输入到tuple构造函数中。

def copy_tree(tree):
    # generator expression - only run when consumed by tuple later on
    children = (
        copy_tree(child) if isinstance(child, tuple) else child
        for child in tree
    )
    return tuple(children)

Of course, you can also directly put the generator expression inside the tuple . 当然,您也可以将生成器表达式直接放在tuple

I think that the intention of the question was to use some sort of recursion. 我认为该问题的目的是使用某种递归。 The following code would deep copy the tree recursively. 以下代码将递归复制树。

def copy_tree(tree):
    new_tree = ()
    for node in tree:
        if type(node) == tuple:
            new_tree += (copy_tree(node),)
        else:
            new_tree += (node,)
    return new_tree


a = ((3, 2), 1, (4,), 5)
b = copy_tree(a)

print(a[0] is b[0])

and the last print would give you False. 最后打印会给您False。

Here's a simple version that copies the tree by feeding the tuple constructor with a recursive generator. 这是一个简单的版本,它通过向tuple构造函数提供递归生成器来复制树。 To test it, I've written another recursive function compare_trees , which you can use to check that corresponding tuples aren't identical (using is ), and that corresponding inner items have the same value (using == ). 为了测试它,我编写了另一个递归函数compare_trees ,可以用来检查对应的元组不相同(使用is ),以及对应的内部项具有相同的值(使用== )。

def copy_tree(tree):
    return tuple(copy_tree(u) if isinstance(u, tuple) else u for u in tree)

def compare_trees(tree1, tree2, indent=''):
    print(indent, 'tree1', tree1, 'tree2', tree2, 'identical', tree1 is tree2)
    indent += '  '
    for u1, u2 in zip(tree1, tree2):
        if isinstance(u1, tuple) and isinstance(u2, tuple):
            compare_trees(u1, u2, indent)
        else:
            print(indent, 'item1', u1, 'item2', u2, 'equal', u1 == u2)

a = ((3, 2), 1, (4,), 5, (6, (7, 8)))
b = copy_tree(a)
print(b)
compare_trees(a, b)

output 输出

((3, 2), 1, (4,), 5, (6, (7, 8)))
 tree1 ((3, 2), 1, (4,), 5, (6, (7, 8))) tree2 ((3, 2), 1, (4,), 5, (6, (7, 8))) identical False
   tree1 (3, 2) tree2 (3, 2) identical False
     item1 3 item2 3 equal True
     item1 2 item2 2 equal True
   item1 1 item2 1 equal True
   tree1 (4,) tree2 (4,) identical False
     item1 4 item2 4 equal True
   item1 5 item2 5 equal True
   tree1 (6, (7, 8)) tree2 (6, (7, 8)) identical False
     item1 6 item2 6 equal True
     tree1 (7, 8) tree2 (7, 8) identical False
       item1 7 item2 7 equal True
       item1 8 item2 8 equal True

I suppose that it's a little ironic that the test code is larger and more complex than the code we want to test, but sometimes that's inevitable. 我想具有讽刺意味的是,测试代码比我们要测试的代码更大,更复杂,但这有时是不可避免的。 ;) ;)

You should use the copy module . 您应该使用复制模块

This module has two functions to fit your needs, copy and deepcopy. 该模块有两个功能可以满足您的需求:复制和深度复制。 copy does the same than copy_tree. 复制的功能与copy_tree相同。

For example if you do : 例如,如果您这样做:

import copy
l = [[0],[0]]
l2 = copy.copy(l)
l[0] = [1]
l[1][0] = [1]

Then l2 will be [[0],[1]] The first element of l2 has not changed because you replaced l first element. 那么l2将是[[0],[1]] l2的第一个元素没有更改,因为您替换了l个第一个元素。 However, the second element has changed because you mutated the second element of l, which is a reference to the element of l2. 但是,第二个元素已更改,因为您突变了l的第二个元素,该元素是对l2的引用。

If you use deepcopy: 如果您使用Deepcopy:

 import copy
 l = [[0],[0]]
 l2 = copy.deepcopy(l)
 l[0] = [1]
 l[1][0] = [1]

Then l2 will be [[0],[0]] because l and l2 does not share anything in common anymore. 那么l2将是[[0],[0]],因为l和l2不再共享任何东西。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM