0

I'm having a problem using lists of objects in python:

I'm declaring a list of objects as follows:

chain = [Node(1), Node(2), Node(3),
     Node(4), Node(5), Node(6),
     Node(7), Node(8), Node(9)]

With the Node being a simple class :

class Node:

def __init__(self, data, links = []):
    self.node_links = links
    self.node_data = data

def add_link(self, link):
    self.node_links.append(link)
(...)
def to_str(self):
    s = "node nb " + str(self.node_data) + " linked to:"
    for link in self.node_links:
        s += " Node " + str(link.node_to.node_data)
    return s
(...)

All is ok from here on, except when I use the method add_link() to an element in my list "chain", it executes the method on all the elements of my chain.

For example,

chain[0].add_link(Link(chain[3], 0.25))

for e in chain:
    print(e.to_str())

Will output

node nb 1 linked to: Node 4
node nb 2 linked to: Node 4
node nb 3 linked to: Node 4
node nb 4 linked to: Node 4
node nb 5 linked to: Node 4
node nb 6 linked to: Node 4
node nb 7 linked to: Node 4
node nb 8 linked to: Node 4
node nb 9 linked to: Node 4

All the elements of my list seem to have been affected by my statement.

I'm pretty baffled by this. I've tried searching everywhere (code & internet) yet I can't find what I've done wrong. Does anyone have an idea?

Jurgen Palsma
  • 300
  • 1
  • 3
  • 16
  • http://docs.python-guide.org/en/latest/writing/gotchas/#mutable-default-arguments – Michael Butscher Oct 16 '17 at 00:49
  • It may seem strange that Python has this behaviour, but it can come in useful in some situations, eg to act as a cache. Still, if you _do_ want to use a mutable default arg it's a Good Idea to comment that you're intentionally invoking that behaviour. Otherwise, people reading your code may assume that it's a bug. – PM 2Ring Oct 16 '17 at 01:38
  • Thanks to all of you. Indeed, even my IDE warned me about mutable arguments, as a noob I ignored the warning, I should've looked into it. – Jurgen Palsma Oct 16 '17 at 13:31

1 Answers1

0

As per the link Michael Butscher provided in the comment, when you do

def __init__(self, data, links = []):

The same [] is used, so all your nodes are referencing the same list. You can change your constructor to:

def __init__(self, data, links=None):
    self.node_links = links or []
    self.node_data = data
logee
  • 5,017
  • 1
  • 26
  • 34