11

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

2 Answers 2

15

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

Sign up to request clarification or add additional context in comments.

4 Comments

Thank you Devin! Both your and Felix' solution seem to have a side effect I haven't considered: The new class is an object and needs to be hooked to something - stored in a varibale or appended to a list or something. I can't just create the Knight class and then call the constructor with myKnight = Knight(). Rather, I have to do something like this: unitlist.append(meta("Knight")) myKnight = unitlist[0]() Is there a way arround this? To put it bluntly: Save the Knight class at the same "general position" the Unit class is saved when it's definition is reached in the code?
@Sano98: I'm really not sure what you mean. You can do Knight = meta("Knight") and then do myKnight = Knight(). What do you want, exactly?
Thanks, that's exactly what I want to do! Simply save it under Knight... And then it's in the global namespace and I can use the class as if it was hardcoded. Great, thank you. Just didn't think I could call Knight() when saving the class as Knight, but sure, why not?
Looking back on this, I'd like to note that Felix's idea of giving a "name attribute" is probably the right answer.
7

Have a look at the type() builtin function.

knight_class = type('Knight', (Unit,), {})
  • First parameter: Name of new class
  • Second parameter: Tuple of parent classes
  • Third parameter: dictionary of class attributes.

But in your case, if the subclasses don't implement a different behaviour, maybe giving the Unit class a name attribute is sufficient.

1 Comment

I found your answer very helpful. Thanks for adding it in spite of the others already being there!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.