0

I'm trying to implement a R-Tree datastructure and i have the following RNodeLeaf class:

class RNodeLeaf(object):
def __init__(self, entries=[]):
    self._entries = entries

def addEntry(self, e):
    self._entries.append(e)

And i want to distribute some data to the nodes i created. consider this piece of code:

        nodes = []
        for i in range(8):

            if i % 4 == 0:
                node = RNodeLeaf()
                nodes.append(node)
            node.addEntry(5)

        print(nodes)
        print(nodes[0]._entries)
        print(nodes[1]._entries)

So my assumption would be that there were 2 RNodeLeafs created which is true, as i printed print(nodes) and both of them have 4 elements. But then after i print print(nodes[x]._entries) i was surprised, because they both the same data.

[5,5,5,5,5,5,5,5]

So what am i missing and what can be done to fix this error?

greedsin
  • 1,252
  • 1
  • 24
  • 49
  • 2
    almost certainly this: https://stackoverflow.com/questions/1132941/least-astonishment-and-the-mutable-default-argument – Corley Brigman Jul 12 '17 at 14:54
  • 2
    and to fix it, change `entries=[]` to `entries=None`, and add `if entries is None: entries = []` – Corley Brigman Jul 12 '17 at 14:54
  • The `nodes=[]` is the problem, but I’m too lazy to find the source to mark it as a duplicate. It gets evaluated once, so it’s always the same list. Verify with `nodes[0]._entries is nodes[1]._entries`—they are the exact same object in memory, not just equal. – Arya McCarthy Jul 12 '17 at 14:55
  • 1
    Possible duplicate of ["Least Astonishment" and the Mutable Default Argument](https://stackoverflow.com/questions/1132941/least-astonishment-and-the-mutable-default-argument) – Arya McCarthy Jul 12 '17 at 14:56

2 Answers2

1

I tried it out and get the same result. If you call the init() this way, it will work:

node = RNodeLeaf(entries=[])

I think, it's because every variable is global and public, but I'm not sure. You can read about default parameters here. When you call init() the first time it creates 'entries' and when you make this: self._entries = entries it doesn't create a new object, it just get a pointer to 'entries'. So at node.addEntry(5), it add it to 'entries'. The second node does the same, get a pointer to 'entries' and this is why you get 2 nodes with the same list.

1

Your problem is that you make an argument default to []. Read this post signaled as a duplicate. Basically, the list at the __init__ line defined as [] is the same everywhere in your code. As a consequence, a modification of entries in a node will be seen in the entries attribute of every other node.

So you need to change your __init__ from:

def __init__(self, entries=[]):
    self._entries = entries

to:

def __init__(self, entries=None):
    if entries is None:
        entries = []
    self._entries = entries

What you need to remember: never make an argument default to a mutable object.

Right leg
  • 16,080
  • 7
  • 48
  • 81