Python: 为什么 __str__ 方法中的这个 AttributeError 在这个缩进改变后就消失了

Python: Why does this AttributeError in __str__ method disappear after this indentation change

我希望您能够在您的机器上重现此错误。当我将 __str__ 方法与与方法相同的缩进对齐时,我无法弄清楚为什么下面的代码会给我一个错误 [此后粘贴]:__init__addNeighbor。我已经从这个 link 中编写了 class 的一部分,但在 __str__ 中以不同的方式命名了我的变量。当我收到错误时,我只是粘贴了 link 中的代码并且它起作用了。

正是在这一点上,我意识到我对 __str__ 方法的缩进与其他方法的缩进不一致。当我将所有内容都放入相同的缩进中时,我遇到了同样的错误,所以我将该方法返回到其先前的实现中并得到以下输出:<__main__.Vertex object at 0x1079b4128> 为什么会这样?我不应该从一开始就得到一个 IndentationError 吗?当我缩进我自己版本的 __str__ 方法并给它更多的缩进时,我没有收到任何错误。为什么?谁能复制这个错误?

class Vertex:
  def __init__(self, key):
    self.id = key
    self.connectedTo = {}

  def addNeighbor(self, neighbor, weight=0):
    self.connectedTo[neighbor] = weight

    def __str__(self):
      return str(self.id) + " connected to: " + str([vertex.id for vertex in self.connectedTo])

    # def __str__(self):
    #   return str(self.id) + ' connectedTo: ' + str([x.id for x in self.connectedTo])

if __name__ == '__main__':
  vertex = Vertex(1)
  vertex.addNeighbor('1', 20)
  vertex.addNeighbor('2', 10)
  print(vertex)

我得到的错误是:

Traceback (most recent call last):
  File "graph_implementation.py", line 19, in <module>
    print(vertex)
  File "graph_implementation.py", line 13, in __str__
    return str(self.id) + ' connectedTo: ' + str([x.id for x in self.connectedTo])
  File "graph_implementation.py", line 13, in <listcomp>
    return str(self.id) + ' connectedTo: ' + str([x.id for x in self.connectedTo])
AttributeError: 'str' object has no attribute 'id'

这是因为使用缩进,__str__ 是方法 addNeighbor 中的局部函数。仅在addNeighbor范围内可见。如果没有自定义__str__,则使用默认方法

原来的错误是因为x.id中的x是一个字符串(节点名)。字符串没有 id 属性。为了使您的代码正常工作,请将 vertex.addNeighbor('1', 20) 等更改为 vertex.addNeighbor(Vertex('1'), 20)

这里发生了一些事情。

首先,正如您所说,__str__的缩进是错误的。但它仍然是 有效 缩进,这就是为什么你不会得到 IndentationError 的原因。该错误仅在调用 __str__ 时出现,只有当它处于正确的缩进级别时才会发生,这是较小的缩进,符合 __init__addNeighbor。 (否则,它只是 addNeighbor 中的一个局部函数,而且毫无用处。)

调用 __str__ 方法后,您得到的错误是由于 self.connectedTo 字典的结构。在 addNeighbor() 中,您正在映射 neighbor: weight,其中 neighbor 作为 str 传入,权重作为 int 传入。当您像 [x.id for x in self.connectedTo] 那样遍历字典时,Python 将遍历该字典的 keys,在本例中为 neighbor,a细绳。所以 x 变成了一个字符串。 (list comprehension 写成[x.id for x in self.connectedTo.keys()]比较好,和原来的一样,但是更清晰。)

您链接的页面显示了最初的错误,即 neighbor 应该是另一个 Vertex,而不是 str。这意味着这一切都归结为将错误类型的参数传递给 addNeighbor()。因此,考虑到这一点,我可能会像这样重写您的测试代码...

class Vertex:
  def __init__(self, key):
    self.id = key
    self.connectedTo = {}

  def addNeighbor(self, neighbor, weight=0):
    self.connectedTo[neighbor] = weight

  def __str__(self):
    return str(self.id) + ' connectedTo: ' + str([x.id for x in self.connectedTo])

if __name__ == '__main__':
  vertex = Vertex(1)
  vertex.addNeighbor(Vertex(1), 20) # This creates a second Vertex with the same ID
                                    # as `vertex` -- probably not what you intend?
  vertex.addNeighbor(Vertex(2), 10)
  print(vertex)


# Output:
1 connected to: [1, 2]