Question on object tree nesting

Hi all, first time posting, pls no flame and all that :slight_smile:
I’ve made an object tree for a project I’m doing and I’ve noticed some unexpected behavior when adding new children to a parent object. What happens is that when using append() to add an object to an empty children list of a parent, the child gets added to all objects of that class. This means that the child also is its own child, which is bad news for anyone trying to make a tree hierarchy.

The simplest reproducable version I could make was as such:

class Node:
  children = []
  
a = Node()
b = Node()
c = Node()

a.children.append(b)
d = Node()

print(a)
print(b)
print(c)
print(d)
print(a.children)
print(b.children)
print(c.children)
print(d.children)

With the following output:

<__main__.Node object at 0x7f2941eb3af0>
<__main__.Node object at 0x7f2941eb3760>
<__main__.Node object at 0x7f2941eb37c0>
<__main__.Node object at 0x7f2941eb3d00>
[<__main__.Node object at 0x7f2941eb3760>]
[<__main__.Node object at 0x7f2941eb3760>]
[<__main__.Node object at 0x7f2941eb3760>]
[<__main__.Node object at 0x7f2941eb3760>]

As can be seen, Node b is now a child of every Node created, even Nodes created after appending.

My question is: is this intended behavior? If so, for what purposes? Because this doesn’t happen when appending to lists with length >= 1, and one can use a.children = [b] for the empty list case to only add to a.

Yes.

children defined in this way is at the class level, a class attribute. Not only can you reach it from every instance of the class, every instance “has it”.

Because when you write it this way, it is a different variable to the one defined before. It now is a instance variable of a and is not an attribute of any other instance of Node. Moreover, it shadows the class variable of the same name, because first the instance is used to look up an attribute and if not found, Python continues to search the class (and ancestor classes if necessary).

1 Like

[… legit code snipped…]

As mentioned, this defines a single “children” on the Node class
itself. When you go obj.attr, it will find attr on obj, but if it
isn’t present it will look on the class.

This lets you do things like:

class Node:

    DEFAULT_NAME = "fred"

and later access that via a Node:

print(a.DEFAULT_NAME)

without needing to explicitly know a's class.

Usually you’re defining per-instance state, so you want this kind of
setup:

class Node:

    def __init__(self):
        self.children = []

Here, init is called automatically whenever you make a new Node in
order to intialise it. It is automatically passed the instance as self
just like any other method, and we create a fresh empty list [] for
each initialisation.

Cheers,
Cameron Simpson cs@cskk.id.au

1 Like

This happens because you declared children as a class attribute, not an instance attribute.
Thus you have only one list for all instances. To correct the problem, you need to code an init (constructor) method, and have it do something like:
self.children =

1 Like