How to set class names dynamically?
I have a function that creates classes derived from it's arguments:
def factory(BaseClass) :
class NewClass(BaseClass) : pass
return NewClass
Now when I use it to create new classes, the classes are all named the same, and the instances look like they have the same type:
NewA = factory(ClassA)
NewB = factory(ClassB)
print type(NewA()) # <class __main__.NewClass>
print type(NewB()) # <class __main__.NewClass>
Is the proper fix to manually set the __name__
attribute?
N开发者_Python百科ewA.__name__ = 'NewA'
print type(NewA()) # <class __main__.NewA>
Are there any other things I should be setting while I'm at it?
Yes, setting __name__
is the correct thing to do; you don't need to set anything else to adjust the class name.
For example:
def factory(BaseClass) :
class NewClass(BaseClass): pass
NewClass.__name__ = "factory_%s" % BaseClass.__name__
return NewClass
type
is the wrong thing to use here. It doesn't let you define classes with Python's normal class syntax, instead making you set up every class attribute manually. It's used to create classes by hand, e.g. if you have an array of base classes and you want to create a class using it (which you can't do with Python's class syntax). Don't use it here.
Updating the answer off Glenn Maynard: Nowadays there is the __name__
attribute and the __qualname__
attribute. The first is what you might think; the second is the dotted "path" for nested classes.
In case of "simple" classes both are equal. Just set __name__
and __qualname__
to your new name. You should set both attributes, since you cannot be sure at which one 3rd-party code will look.
Now for nested classes, the differences between the two attributes show:
class Outer:
class Inner:
pass
print(Outer.__name__, Outer.__qualname__)
print(Outer.Inner.__name__, Outer.Inner.__qualname__)
prints:
Outer Outer
Inner Outer.Inner
If you want to change Outer
's name, you need to patch three places, namely Outer.__name__
, Outer.__qualname__
, Inner.__qualname__
. For the latter two you need to split and join at the dots correctly.
A final warning: Even if you did all that right, stuff like sphinx, pylint, etc... might still not work 100%. For example the fake name cannot be found in the module namespace as usual; the source cannot be grep
ped for the class definition; and so on.
Check out using the type()
function with three arguments. The following code creates a new class "NewA", with object
as the base type, and no initial attributes.
>>> type('NewA', (object,), {})
<class '__main__.NewA'>
精彩评论