Я работаю над вопросом, который требует от меня определения функции 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.
ваш код не запускается. TypeError: unsupported operand type(s) for +: 'int' and 'tuple'
Вы можете использовать copy.deepcopy или tuple(map(tuple, tree)), если все кортежи, но почему вы вообще хотите копировать кортежи? Кортежи неизменяемы.
нужно сделать a[0]==b[0], думаю, для сравнения значений. Подробнее об этом: stackoverflow.com/questions/132988/…
@hadik, который не сработает в этом случае, поскольку скопированное значение все равно будет сравниваться с самим собой, даже если оно находится в другом адресе памяти.
Фактически, даже copy.deepcopy будет нет создать копию кортежей (is же возвращает True), потому что это не имеет реального смысла.
@hadik @roganjosh да, вопрос конкретно требует, чтобы я написал код таким образом, чтобы использование оператора is возвращало False. Нет проблем, если == возвращает True.
Я в основном согласен с тем, что сказал Тобиас. Одним из преимуществ использования неизменяемых объектов является то, что для них безопасно делиться ссылками. OTOH, кортеж жестяная банка содержит изменяемые объекты, поэтому эта операция глубокого копирования не совсем бесполезна. ;)
@rocksportrocker, ты прав. Извините, я скопировал неправильный код. Обновил его моим последним кодом, который работает и дает визуально правильный результат, но не проходит тест is.
@ PM2Ring Верно, но поскольку код OP не создает копии внутренних объектов и не пытается работать рекурсивно, я предположил, что это не так.
Кортежи @tobias_k не являются полностью неизменяемыми, если их содержимое неизменяемо. например. ([1], [2]). Если вам нужна глубокая копия этого кортежа, вам нужно будет скопировать и его, и его содержимое.
@RyanChng он прошел тест на моем компьютере, какую версию Python вы используете?
@Dunes Я знаю об этом, но OP явно пытается скопировать только кортежи, а не их содержимое. См. Мой комментарий выше.
@ toti08 он проходит этот тест и на моем, но не проходит несколько частных тестов на веб-сайте автогрейдера, который я использую. Я думаю, что частные случаи включают больше уровней вложенности.
Поэтому вам следует написать рекурсивную тестовую функцию, которая сравнивает два дерева, применяя тест is на каждом уровне. Таким образом, вы можете гарантировать правильность работы copy_tree.






Вы должны использовать модуль копирования.
Этот модуль имеет две функции, которые соответствуют вашим потребностям: копирование и глубокое копирование. copy делает то же самое, что и 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 больше не имеют ничего общего.
Теперь заставьте его работать с кортежами в соответствии с вопросом. ;)
Простая передача существующего кортежа конструктору 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
Преобразование в list - это хитрый "прием", чтобы tuple не возвращал только ввод, но вы можете добавить кортеж Почему, который в первую очередь ведет себя так.
Вот рекурсивное решение, которое глубоко копирует (вложенные) кортежи, оставляет другие объекты неизменными и не использует модуль 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
Рекурсия - определенно самый элегантный подход, если вы не знаете заранее уровни вложенности.
Использование функции map для рекурсивного вызова очень разумно. Проголосовали.
В вашем коде отсутствует рекурсивный шаг - вы копируете только кортеж верхнего уровня.
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.
Это действительно сработало, спасибо! Я еще не изучил isinstance, но я заменил эту строку на if type(i) == tuple. Я все еще учусь на вводном уровне, поэтому я не думаю, что они ищут следующие решения, которые вы опубликовали, но первое помогает мне хорошо понять. Огромное спасибо!
@RyanChng isinstance обычно предпочтительнее type для подобных вещей. Здесь это не имеет значения, но isinstance более гибкий, поскольку он может тестировать несколько типов за один раз, а isinstance(obj, cls) вернет True, если obj является экземпляром cls или любого дочернего класса cls.
Я думаю, что целью вопроса было использование какой-то рекурсии. Следующий код рекурсивно копирует дерево.
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.
Вот простая версия, которая копирует дерево, передавая конструктору 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
Я полагаю, это немного иронично, что тестовый код больше и сложнее, чем код, который мы хотим протестировать, но иногда это неизбежно. ;)
Вы знаете о модуле
copy?