Bojan Kogoj Bojan Kogoj - 3 months ago 14
Python Question

Infinite recursion when nesting objects

First I should mention I'm rather new to python. I'm trying to put a Container inside a Container, but when I do so, I get infinite number of recursive Containers.

class Container(Drawable):

content = []

# some more code

def append(self, obj):
print(obj.content)
self.content.append(obj)
print(self.content[0].content)


Page is similar to Container. I add some elements

pa = Page()
ca = Container(color="red")
cb = Container(color="blue")
ca.append(cb)
pa.append(ca)


Append, inside container prints the following, which is already incorrect, since both of them should be the same

[]
[<src.test.container.Container object at 0x013550D0>]


I use this method to print it out

class SomeClass():

def __init__(self, page):

self.print_content(page, 0)


def print_content(self, parent, depth):
depth += 1
for obj in parent.content:

print((str(depth).rjust(depth, ' ') + " " + str(obj)).rjust(depth, ' '))

if(depth > 5): # to stop infinite recursion in print
return
self.print_content(obj, depth)


I get the following, which makes no sense. I never added ca inside itself as a content, but it happens when I append it.

1 Container [color=red ]
2 Container [color=blue ]
3 Container [color=blue ]
4 Container [color=blue ]
5 Container [color=blue ]
6 Container [color=blue ]


Any idea why this is happening? If I append two Containers to Page it's fine, but as soon as I nest, it becomes infinite recursion. Also, all Containers with color blue are the same (address). I feel like it's an obvious mistake but I can't figure it out

Answer

On your Container class, you're defining content as a class level attribute.

That means, that even if multiple instances of Container exist, they all share the same mutable content list. As soon as you modify that list (whether you do that with self.content.append() or Container.content.append()), you're changing that list for all those objects.

Initialize content as an instance attribute in __init__() instead:

class Container(Drawable):

    def __init__(self, *args, **kwargs):
        super(Container, self).__init__(*args, **kwargs)
        self.content = []

(The super(Container, self).__init__(*args, **kwargs) call is because I don't know what your Drawable class looks like. It might also need to have its intitialiser called, and so we're accepting all positional and keyword arguments here, and just pass them on, before initialising self.content)

Comments