Пустой список в конструкторе класса Python вызывает ошибку

Я создаю простое дерево, в котором каждый узел имеет любое количество дочерних элементов в Python, и я создал класс узла, чтобы помочь мне. Каждый узел содержит ссылку на свой родительский узел (int) и любые дочерние узлы (список).

Однако явное добавление пустого списка в аргумент конструктора узла дало мне странные результаты, и мне хотелось бы получить объяснение, почему это поведение изменяется, когда список явный или явно не помещен в аргументы конструктора:

Реализация №1:

      class Node:
    def __init__(self, value, parent, children=[]):
        self.parent = parent
        self.value = value 
        self.children = children

Реализация №2:

      class Node:
    def __init__(self, value, parent):
        self.parent = parent
        self.value = value 
        self.children = []

Чтобы заполнить массив узлов:

      parents = [4,-1,4,1,1]
nodes = [None] * n
for i in range(n):
    nodes[i] = Node(i, parents[i])

Чтобы сохранить родительский атрибут каждого узла:

      tree = Tree()
for i, node in enumerate(nodes):
    parent_id = node.parent
    if parent_id == -1: 
        tree.root = nodes[i]
    else:
        nodes[parent_id].children.append(node.value)

print([(node.value, node.children) for node in nodes])

С Реализацией №1 я получаю:

      [(0, [0, 2, 3, 4]), (1, [0, 2, 3, 4]), (2, [0, 2, 3, 4]), (3, [0, 2, 3, 4]), (4, [0, 2, 3, 4])]

но с Реализацией № 2 я (правильно) получаю:

      [(0, []), (1, [3, 4]), (2, []), (3, []), (4, [0, 2])]

В чем разница? Я не понимаю, почему список полностью заполнен для каждого узла даже с if а также elseзаявления. Любая помощь приветствуется, в том числе если вы думаете, что есть лучшие способы сделать это.

1 ответ

Аргументы по умолчанию связываются один раз при определении функции, поэтому каждый объект Nodeполучает тот же объект списка в вашей первой реализации.

Локальные переменные оцениваются при запуске функции, поэтому self.children=[] назначает новый список каждому объекту.

Лучшим подходом, если вы хотите разрешить необязательный аргумент, было бы

      class Node:
    def __init__(self, value, parent, children=None):
        self.parent = parent
        self.value = value 
        self.children = children or []

Это использует Noneкак значение по умолчанию. В or оператор позволяет нам выбрать children если аргумент истинный, и пустой список, если он ложный.

Из документации.

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