Home > Back-end >  Unexpected behavior with python list of dictionaries when adding new key to them
Unexpected behavior with python list of dictionaries when adding new key to them

Time:08-30

World! I've encountered an unexpected behavior from my python interpreter during a project. I'm aware that, in certain situations, python uses the reference to an object, not the object itself. (for example: here) However, I cannot explain why this problem arises in this piece of code since the elements of the list seem to be different instances with different object ids. Whenever I add a new {key:value} pair to the dictionary, all other dictionaries in that list get updated.

class Node():
    def __init__(self, name, neighbors=dict()):
        self.name = name
        self.neighbors = neighbors # a dict containing all neighboring nodes in form of {'name':value} pairs

    def add_neighbor(self, neighbor, value=0):
        self.neighbors[str(neighbor)] = value


if __name__ == "__main__":
    n_1 = Node(name='node_1')
    n_1.add_neighbor('node_2', value=5)
    n_2 = Node(name='node_2')

    node_list = [n_1, n_2]
    for node in node_list:
        print(id(node), node.name, node.neighbors)
output:

2129022374520 node_1 {'node_2': 5}
2129022374576 node_2 {'node_2': 5}

CodePudding user response:

class Node():
    def __init__(self, name, neighbors=dict()):

This code is being executed when the class is parsed/interpreted. In particular, the default value for neighbors is a single dictionary that is shared across class instances. A typical pattern for what you're trying to do is to instead write:

class Node():
    def __init__(self, name, neighbors=None):
        neighbors = neighbors or dict()

CodePudding user response:

A really interesting question actually...

This is because the same dict() is assigned to both (in fact all instances of the object). If it was removed (for example replaced with a type hint) then this would work and you can do the dict allocation in the code.

So this would work:

class Node():
    def __init__(self, name, neighbors:dict):  # <--- change to a type hint
        self.name = name
        self.neighbors = neighbors # a dict containing all neighboring nodes in form of {'name':value} pairs

    def add_neighbor(self, neighbor, value=0):
        self.neighbors[str(neighbor)] = value


if __name__ == "__main__":
    n_1 = Node(name='node_1', neighbors={})  # <--  dict is here
    n_1.add_neighbor('node_2', value=5)
    n_2 = Node(name='node_2', neighbors={})  # <-- a different dict is here

    node_list = [n_1, n_2]
    for node in node_list:
        print(id(node), node.name, node.neighbors)

And this is the result:

2716705976384 node_1 {'node_2': 5}
2716712272224 node_2 {}
  • Related