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)
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.