Передача Python по значению ссылки на объект
Я пытаюсь написать алгоритм на python, чтобы распечатать все пути от корня (двоичного) дерева до каждого листа. Вот мой код:
def fb_problem(node, curr_trav):
curr_trav = curr_trav + [node]
if node.left is None and node.right is None:
for path_node in curr_trav:
print path_node.data
print "XXX"
if node.left is not None:
fb_problem(node.left, curr_trav)
if node.right is not None:
fb_problem(node.right, curr_trav)
fb_problem(root, [])
Я сохраняю список узлов в текущем обходе, и когда я достигаю листа, я распечатываю список. Я не совсем понимаю, как Python передает объекты, хотя. Я думал, что, когда каждый рекурсивный вызов завершается и извлекается из стека, оригинал curr_trav
переменная не будет зависеть от того, что сделал рекурсивный вызов. Тем не менее, кажется, что линия
curr_trav += [node]
Мутирует оригинальный список. +=
Оператор возвращает новый список, в отличие от .append()
, который на самом деле мутирует оригинальный объект. Так не должен ли этот вызов просто переназначить имя, данное объекту в функции, а не поменять исходный объект? Когда я меняю строку на что-то вроде
t_trav = curr_trav += [node]
Все отлично работает, но я не понимаю, в чем проблема с оригинальной линией. Пожалуйста, дайте мне знать, если мой вопрос неясен.
2 ответа
Ваше понимание +=
не совсем правильно. Все операторы в Python - это просто ярлыки. Например, a + b
является a.__add__(b)
если a
имеет __add__
метод. Если a
нет, это b.__radd__(a)
, Если b
нет такого метода, возникает ошибка. Обычно, a += b
ведет себя совсем как a = a + b
, но в случае изменяемых объектов это обычно не так. Это потому a += b
является a.__iadd__(b)
если a
имеет __iadd__
метод. Если a
нет, это так же, как a = a.__add__(b)
, Если a
этого тоже нет, это то же самое, что a = b.__radd__(a)
, Поскольку списки имеют __iadd__
метод, фактический объект списка изменяется вместо переопределения curr_trav
,
С питоном это ни по значению, ни по ссылке. Это комбинация обоих и зависит от типа объекта, передаваемого в функцию. Например, если изменяемый тип, такой как dict, list etc
Передача в нем пройдет ссылка. В то время как с неизменным типом, таким как str
это будет по значению. Хорошее чтение на эту тему - Джефф Кнупп.
Проблема с вашим оригинальным кодом curr_trav += [node]
является то, что он добавляет значения [node]
в curr_trav
и установка ссылки на новый список. Потому что он передает ссылку на curr_trav
это будет изменяться через каждую последующую итерацию.