在 Python 中,当使用列表作为类的属性时,对象标识可能会出现问题。例如,下面的代码创建了一个包含多个节点的树,每个节点都有一个名称和一个子节点列表:
class Node(object):
def __init__(self, name, children=[]):
self.name = name
self.children = children
def add_child(self, child):
self.children.append(child)
def __str__(self):
return self.name
def create_tree(num_vertices):
vs = [Node(str(i)) for i in range(num_vertices)]
for i in range(num_vertices):
if 2 * i + 1 < num_vertices:
vs[i].add_child(vs[2 * i + 1])
if 2 * i + 2 < num_vertices:
vs[i].add_child(vs[2 * i + 2])
return vs[0]
def bfs(top_node, visit):
"""Breadth-first search on a graph, starting at top_node."""
visited = set()
queue = [top_node]
while len(queue):
curr_node = queue.pop(0) # Dequeue
visit(curr_node) # Visit the node
visited.add(curr_node)
# Enqueue non-visited and non-enqueued children
queue.extend(c for c in curr_node.children
if c not in visited and c not in queue)
def visit(tree):
print(tree)
如果我们尝试多次调用 create_tree() 函数来创建新的树,我们会发现这些树实际上并不是新的,而是之前的树的副本。这是因为在 Node 类的 __init__() 方法中,我们将 children 属性初始化为空列表 []。这意味着每次调用 Node 类的构造函数时,都会使用同一个空的列表作为 children 属性,而不会创建一个新的列表。
- 解决方案
为了解决这个问题,我们需要在
Node类的__init__()方法中创建一个新的列表作为children属性。我们可以通过将children属性的默认值设置为None来实现这一点,如下所示:
class Node(object):
def __init__(self, name, children=None):
self.name = name
if children is None:
children = []
self.children = children
def add_child(self, child):
self.children.append(child)
def __str__(self):
return self.name
def create_tree(num_vertices):
vs = [Node(str(i)) for i in range(num_vertices)]
for i in range(num_vertices):
if 2 * i + 1 < num_vertices:
vs[i].add_child(vs[2 * i + 1])
if 2 * i + 2 < num_vertices:
vs[i].add_child(vs[2 * i + 2])
return vs[0]
def bfs(top_node, visit):
"""Breadth-first search on a graph, starting at top_node."""
visited = set()
queue = [top_node]
while len(queue):
curr_node = queue.pop(0) # Dequeue
visit(curr_node) # Visit the node
visited.add(curr_node)
# Enqueue non-visited and non-enqueued children
queue.extend(c for c in curr_node.children
if c not in visited and c not in queue)
def visit(tree):
print(tree)
现在,当我们多次调用 create_tree() 函数时,我们将会得到不同的树,因为每次调用都会创建一个新的列表作为 children 属性。