I hope you will be able to replicate this error on your machine. I couldn't figure out why exactly the code below gave me an error [pasted thereafter] when I had the __str__
method aligned with the same indentation as the methods: __init__
and addNeighbor
. I had written part of the class from this link but named my variables in __str__
differently. When I got the error, I just pasted the code from the link and it worked.
It was at this point that I realized that my indentation for the __str__
method was not inline with that of the other methods. When I put everything into the same indentation, I had the same error so I returned the method into its prior implementation and got this output: <__main__.Vertex object at 0x1079b4128>
Why is this the case? Shouldn't I have gotten an IndentationError
from the beginning? When I indent my own version of the __str__
method and give it more of an indentation, I don't get any error. Why? Can anyone replicate this error?
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)
The error I get is:
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'
There are a few things going on here.
First of all, as you said, your indentation of __str__
is wrong. But it's still a valid indentation, which is why you wouldn't get an IndentationError
. The error only appears when __str__
gets called, which only happens when it's at the correct indentation level, which is the lesser indentation, in line with __init__
and addNeighbor
. (Otherwise, it's just a local function inside addNeighbor
, and pretty useless.)
Once the __str__
method is being called, the error you are getting is because of the structure of the self.connectedTo
dictionary. In addNeighbor()
you are mapping neighbor: weight
, where neighbor
is getting passed in as a str
and weight as an int
. When you iterate through a dictionary as in [x.id for x in self.connectedTo]
, Python will iterate through the keys of that dictionary, which in this case is neighbor
, a string. So x
becomes a string. (The list comprehension is better written as [x.id for x in self.connectedTo.keys()]
, which is equivalent to the original but clearer.)
The page you linked shows the original mistake, though – namely that neighbor
should be another Vertex
, not a str
. Meaning it all boils down to passing the wrong type of argument into addNeighbor()
. So with that in mind, I might rewrite your test code like this...
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]