Передача 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 это будет изменяться через каждую последующую итерацию.

Другие вопросы по тегам