связанная структура python — вставка дочернего элемента в родительский узел A также вставляется в узел A

#python #tree #optional-parameters #optional-arguments

#python #дерево #необязательно-параметры #необязательно-аргументы

Вопрос:

Я пытаюсь реализовать связанную структуру для дерева, используя python, это выдержка из моего кода

 class Node(object):
    def __init__(self,value,father=None,children=[]):
        self.value=value
        self.father=father
        self.children=children
    def __str__(self):
        return self.value
  
 class Tree(object):

    def __init__(self,root=None):
        self.root=root
    def insertNode(self,new_node,father_value):
        father_node=self.find_node(father_value)
        if not father_node:
            return False
        new_node.father=father_node
        print("Node's information BEFORE adding child to father's list of children")
        print(f"Father node (find) {father_node}")
        print(f"Father node's children {[x.value for x in father_node.children]}")
        print(f"New node's father {new_node.father}")
        print(f"New node's children {[x.value for x in new_node.children]}")
        father_node.children.append(new_node)
        print("Node's information AFTER adding child to father's list of children")
        print(f"Father node (find) {father_node}")
        print(f"Father node's children {[x.value for x in father_node.children]}")
        print(f"New node's father {new_node.father}")
        print(f"New node's children {[x.value for x in new_node.children]}")

    def find_node(self,value):
        stack=[self.root]
        while True:
            if len(stack)==0:
                return False
            node=stack.pop()
            if node.value==value:
                return node
            children=node.children
            stack.extend(children)
  
 n1=Node("A")
tree=Tree(n1)
tree.insertNode(Node("B"),"A")

n1=Node("A")
tree=Tree(n1)
tree.insertNode(Node("B"),"A")
  

ВЫВОД

Информация об узле ПЕРЕД добавлением дочернего элемента в список дочерних элементов отца
Родительский узел (найти) A Дочерние элементы родительского узла [] Отец нового узла A Дочерние элементы нового узла [] Информация об узле ПОСЛЕ добавления дочернего элемента в список дочерних элементов отца Родительский узел (найти) A Дочерние элементы родительского узла ['B'] Отец нового узла A Дочерние элементы нового узла ['B']

Как вы можете видеть, когда я добавляю новый вставленный узел в список дочерних элементов отца, он также вставляется в список дочерних элементов нового узла. Как это исправить?

Ответ №1:

Проблема возникает из-за того, как Python обрабатывает значения по умолчанию необязательных параметров функции.

В class Node(object) , у вас есть эта строка:

 def __init__(self,value,father=None,children=[]):
  

Python вычисляет значение по умолчанию [] только один раз (создавая пустой list объект), а затем повторно использует это оцененное значение (ссылку на этот list объект) для всех последующих вызовов этой функции.

В вашем случае функция — это __init__() функция, которая вызывается каждый раз, когда вы создаете экземпляр Node класса. Если вы создаете экземпляр Node несколько раз без явной передачи третьего аргумента, во всех этих вызовах __init__() параметру children присваивается тот же list объект, что и значение по умолчанию. Вы используете это значение для установки children атрибута всех этих экземпляров. Другими словами, для всех этих Node экземпляров children атрибут будет указывать на один и тот же list объект. Вот почему вставка дочернего элемента в «отца» Node также добавляет того же дочернего элемента к текущему Node .

Правильный способ перезаписи __init__() будет:

 class Node(object):
    def __init__(self,value,father=None,children=None):
        self.value=value
        self.father=father
        self.children=children if children is not None else []
  

Мораль

Общая мораль здесь заключается в том, что, если значение вашего параметра по умолчанию является изменяемым объектом, оно может преподнести сюрпризы, подобные этому — где, неизвестно вам, хранятся несколько ссылок на один объект, и мутации (изменения) объекта через одну ссылку также будут отражены (видны) черездругие ссылки.

Все это задокументировано здесь