Question

I have a list of classes, and I'm trying to dynamically create another list of classes, such that each one has a classmethod that creates an instance of a child of a class from the original list.
The problem is that all the methods end up returning an instance of the same class (the last one created).

Here is a minimized version of my code:

class C1():
    var = 1
class C2():
    var = 2

parents = (C1, C2)
children = []
wrappers = []

for c in parents:

    class ChildClass(c):
        pass
    children.append(ChildClass())

    class Wrapper():
        @classmethod
        def method(cls):
            cls.wrapped = ChildClass()

    wrappers.append(Wrapper)

print(list(child.var for child in children))

for wrapper in wrappers:
    wrapper.method()

print(list(wrapper.wrapped.var for wrapper in wrappers))

The output:

[1, 2]
[2, 2]

You can see that the children list contains different instances, while the classmethod creates an instance of a child of C2 in both cases.

How can I fix my code so that each classmethod creates an instance of the correct class?

(I'm using python 2.7.4)

Was it helpful?

Solution

Your ChildClass reference in the Wrapper.method() is a free variable, meaning it'll be resolved when Wrapper.method() is called, not when the method is defined.

By the time you call that method, the name ChildClass will refer to the last class you created.

You either need to provide a scope that has only one unambiguous value for ChildClass, or otherwise bind the reference at definition time. The latter can be done by using a function parameter default:

class Wrapper():
    @classmethod
    def method(cls, child_class=ChildClass):
        cls.wrapped = child_class()

You can also use a function scope to bind the reference to a local variable in that scope:

def createClasses(parent):
    class ChildClass(parent):
        pass

    class Wrapper():
        @classmethod
        def method(cls):
            cls.wrapped = ChildClass()

    return ChildClass, Wrapper

for c in parents:
    child, wrapper = createClasses(c)

    children.append(child)
    wrappers.append(wrapper)

Here, when Wrapper.method refers to ChildClass, it'll be looked up in the local namespace of the createClasses function, and there is only ever going to be one value bound to that name.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top