stadicate
stadicate

Reputation: 13

Attribute error in Python when trying to append multiple values to key in dictionary

So I've been trying to work out this error for awhile now and I can't seem to figure out what the problem is. All I want is to have a dictionary with various keys that can be associated with multiple values. I keep getting this error:

Traceback (most recent call last):
File "C:/Users/.py", line 34, in insert_nodes
self.__graph_dict[value].append(node2)
AttributeError: 'Node' object has no attribute 'append'

It's entirely possible I'm overlooking something small or just really confused about how I should be approaching this issue. My code is as follows:

class Node(object):

    def __init__(self, value, colored):
        self.value = value
        self.colored = colored

class Graph(object):

    def __init__(self):
        self.graph_dict = {}

    def nodes(self):
        return self.nodes.keys()

    def insert_nodes(self, value, neighbor):
        node1 = Node(value, colored="blank")
        node2 = Node(neighbor, colored="blank")
        if value in self.graph_dict:
            self.graph_dict.setdefault(value, [])
            self.graph_dict[value].append(node2)
        else:
            self.graph_dict[value] = node2

    def populate_graph(self):
        graph_name = input("Enter your graph name ")
        f = open(graph_name, 'r').readlines()
        num_vertices = f.pop(0)

        for lines in f:
            pairs = lines.split(" ")
            self.insert_nodes(int(pairs[0]), int(pairs[1].rstrip()))

g = Graph()
g.populate_graph()

Any help would be appreciated.

Upvotes: 1

Views: 452

Answers (2)

MariusSiuram
MariusSiuram

Reputation: 3644

You could take advantage of collections.defaultdict as follows:

from collections import defaultdict

(...)
    def __init__(self):
        self.graph_dict = defaultdict(list)

(...)
    def insert_nodes(self, value, neighbor):
        node1 = Node(value, colored="blank")
        node2 = Node(neighbor, colored="blank")
        # no need for the if
        self.graph_dict[value].append(node2)

Another option, and one of the correct ways to use the setdefault in that scenario would be the following:

(...)
def insert_nodes(self, value, neighbor):
    node1 = Node(value, colored="blank")
    node2 = Node(neighbor, colored="blank")
    self.graph_dict.setdefault(value, [])
    self.graph_dict[value].append(node2)

although I don't prefer it, it is valid and readable, IMHO. I still find more appealing the defaultdict approach.

Edit: Or, more or less equivalent but with less lookups:

(...)
def insert_nodes(self, value, neighbor):
    node1 = Node(value, colored="blank")
    node2 = Node(neighbor, colored="blank")
    self.graph_dict.setdefault(value, []).append(node2)

"It is better to ask for forgiveness thant ask for permission"... or something like that. It is considered more pythonic, and it is indeed faster, to just try to do it and catch the exception.

This is more related to style, given that @barny has already fixed the bug. However, I include it for completeness:

(...)
def insert_nodes(self, value, neighbor):
    node1 = Node(value, colored="blank")
    node2 = Node(neighbor, colored="blank")
    try:
        self.graph_dict[value].append(node2)
    catch KeyError:
        self.graph_dict[value] = [node2]

Upvotes: 0

Append() is expecting a list - so the dictionary content of graph_dict isn't a list. This is because when you initialise it you give it a value, not a list. Change:

if value in self.graph_dict:
    self.graph_dict.setdefault(value, [])
    self.graph_dict[value].append(node2)
else:
    self.graph_dict[value] = node2

to:

    if value in self.graph_dict:
## not needed        self.graph_dict.setdefault(value, [])
        self.graph_dict[value].append(node2)
    else:
        self.graph_dict[value] = [node2]

Upvotes: 2

Related Questions