Python: Class factory using user input as class names
factory, metaclass, metaprogramming, python
Solution
To create a class from a name, use the `class` statement and assign the name. Observe:
def meta(name):
class cls(Unit):
pass
cls.__name__ = name
return cls
Now I suppose I should explain myself, and so on. When you create a class using the class statement, it is done dynamically-- it is equivalent of calling `type()`.
For example, the following two snippets do the same thing:
class X(object): pass
X = type("X", (object,), {})
The name of a class-- the first argument to type-- is assigned to `__name__`, and that's basically the end of that (the only time `__name__` is itself used is probably in the default `__repr__()` implementation). To create a class with a dynamic name, you can in fact call type like so, or you can just change the class name afterward. The `class` syntax exists for a reason, though-- it's convenient, and it's easy to add to and change things later. If you wanted to add methods, for example, it would be
class X(object):
def foo(self): print "foo"
def foo(self): print "foo"
X = type("X", (object,), {'foo':foo})
and so on. So I would advise using the class statement-- if you had known you could do so from the beginning, you likely would have done so. Dealing with `type` and so on is a mess.
(You should not, by the way, call `type.__new__()` by hand, only `type()`)
Problem
I want to add class atttributes to a superclass dynamically. Furthermore, I want to create classes that inherit from this superclass dynamically, and the name of those subclasses should depend on user input. There is a superclass "Unit", to which I can add attributes at runtime. This already works. ``` def add_attr (cls, name, value): setattr(cls, name, value) class Unit(object): pass class Archer(Unit): pass myArcher = Archer() add_attr(Unit, 'strength', 5) print "Strenght ofmyarcher: " + str(myArcher.strength) Unit.strength = 2 print "Strenght ofmyarcher: " + str(myArcher.strength) ``` This leads to the desired output: Strenght ofmyarcher: 5 Strenght ofmyarcher: 2 But now I don't want to predefine the subclass Archer, but I'd rather let the user decide how to call this subclass. I've tried something like this: ``` class Meta(type, subclassname): def __new__(cls, subclassname, bases, dct): return type.__new__(cls, subclassname, Unit, dct) factory = Meta() factory.__new__("Soldier") ``` but no luck. I guess I haven't really understood what new does here. What I want as a result here is ``` class Soldier(Unit): pass ``` being created by the factory. And if I call the factory with the argument "Knight", I'd like a class Knight, subclass of Unit, to be created. Any ideas? Many thanks in advance! Bye -Sano