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

时间:2018-09-18 08:04:12

标签: python tuples

我正在研究一个问题,要求我定义一个函数copy_tree,该函数接受参数tree(可能包含元组的元组)并返回树的副本(即存储在不同的内存位置)。

我当前的代码是:

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

但是,这不适用于嵌套的元组,因为内部的元组不是“复制”的,而是被引用的。

例如如果我跑

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

输出必须为False

如何复制元组?

编辑:不允许使用deepcopy模块。

6 个答案:

答案 0 :(得分:3)

这是一个递归解决方案,它可以深层复制(嵌套)元组,保留其他对象不变,并且不使用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

如果您事先不知道嵌套级别,那么递归绝对是最优雅的方法。

答案 1 :(得分:1)

仅将现有元组传递给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)

这样:

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

将输出:

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

答案 2 :(得分:1)

您的代码缺少递归步骤-您仅复制顶级元组。

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

添加递归会生成树的每一层的副本。请注意,您可以仅复制 个元组:

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

递归可修复结果,但是您采用的方法效率低下-创建并丢弃了许多瞬时元组。每次通过+“扩展”一个元组时,都会丢弃旧的元组,并创建一个新的元组。

第一步是延迟创建元组,直到所有子级都被转换为止

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)

由于该列表仅存在,可以变成一个元组,因此我们也可以摆脱它:生成器表达式允许将转换后的子代直接馈送到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)

当然,您也可以将生成器表达式直接放在tuple内。

答案 3 :(得分:0)

我认为问题的意图是使用某种递归。以下代码将以递归方式深度复制树。

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

最后打印会给你False

答案 4 :(得分:0)

这是一个简单的版本,它通过向tuple构造函数提供递归生成器来复制树。为了测试它,我编写了另一个递归函数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)

输出

((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

我想讽刺的是,测试代码比我们要测试的代码更大,更复杂,但这有时是不可避免的。 ;)

答案 5 :(得分:-1)

您应该使用copy module

该模块具有两种功能,可以满足您的需求:复制和深度复制。 复制的功能与copy_tree相同。

例如,如果您这样做:

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

则l2将是[[0],[1]] l2的第一个元素没有更改,因为您替换了l个第一个元素。但是,第二个元素已更改,因为您突变了l的第二个元素,该元素是对l2的引用。

如果您使用Deepcopy:

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

那么l2将是[[0],[0]],因为l和l2不再共享任何东西。