#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 []
Мораль
Общая мораль здесь заключается в том, что, если значение вашего параметра по умолчанию является изменяемым объектом, оно может преподнести сюрпризы, подобные этому — где, неизвестно вам, хранятся несколько ссылок на один объект, и мутации (изменения) объекта через одну ссылку также будут отражены (видны) черездругие ссылки.
Все это задокументировано здесь