Search code examples
pythonclassobjectargumentsdefault

Why are attributes in different objects connected to each other when a default argument is given?


I am implementing a basic node object in python. Basically, I implemented a node class with the attribute f_pointers and set it to the default value []. When ever I try to change f_pointers of (lets say) node_a, I will end up changing f_pointers of node_b, which are programmed to be completely unrelated.

I have already solved the problem by instead changing the default value to None and setting up the forward_pointers in __init__. However, I would still like to know how to avoid this problem in the future and possibly learn something new about Python.

For the sake of simplicity, I removed some unnecessary parts of the code.

class Node:
     def __init__(self, f_pointers = []):
          self.f_pointers = f_pointers
     def get_pointers(self):
          return self.f_pointers
     def add_pointers(self, new_pointer):
          self.f_pointers.append(new_pointer)
a = Node()
b = Node()
print(a.get_pointers, b.get_pointers)

>>> [] []

a.add_pointers("a")
print(a.get_pointers, b.get_pointers)

>> ["a"] ["a"]

a.add_pointers("b")
print(a.get_pointers, b.get_pointers)

>> ["a","b"] ["a","b"]

As can be seen, a and b are completely unrelated objects (other than the fact that they are of the same type Node) but will affect each other. Why does this happen?


Solution

  • It's because you are referencing to the same list (the one instantiated in the __init__ default params list definition like __init__(self, f_pointers=[]). What happens is that when you say in the __init__ method code block that self.f_points = f_pointers you are basically referencing the same list every time you instantiate a new Node object.

    The reasons are explained further here

    What you do want to do instead is instantiate a new list for every init like:

    def __init__(self, f_pointers=None):
        self.f_pointers = []