python dynamic class names
Trying to instantiate a class based on a string value an开发者_StackOverflowd... failing. The parser
object below is a dict
, in the example let's say we have one called foo
and here parser['name']
is 'foo':
obj = parser['name']()
Fails, yielding TypeError: 'str' object is not callable
. But, since I have:
class foo:
def __init__(self():
print 'Hello'
And if I do obj = foo()
it works fine and creates the correct object. Also, calling obj = type(parser['name'])()
doesn't work.
How to resolve this? Update: I don't really want to use a mapping system: the names of these classes are defined INI files, and parsed that way, so they will be strings..
classmap = {
'foo': foo
}
obj = classmap[parser['name']]()
As answered in:
Python dynamic class names
There is an easier way to do this if you know which module the classes are defined in, for example:
getattr(my_module, my_class_name)()
Don't use strings:
parser = {}
class foo:
pass
parser['foo'] = foo
obj = parser['foo']()
You can use a metaclass that stores a dict of known classes:
# a metaclass
class Registry(type):
# store all the types we know
registered = {}
def __new__(cls, name, bases, attrs):
# create the new type
newtype = super(Registry, cls).__new__(cls, name, bases, attrs)
# store it
cls.registered[name] = newtype
return newtype
@classmethod
def class_by_name(cls, name):
# get a class from the registerd classes
return cls.registered[name]
# arbitrary base class for every class that should be in the Register
class Registered(object):
__metaclass__ = Registry
# some classes
class Foo(Registered):
pass
class Bar(Foo):
pass
# get the class object:
print Registry.class_by_name('Foo') # <class '__main__.Foo'>
# it can be instanciated too ofc:
print Registry.class_by_name('Bar')() # <__main__.Bar object at 0x01F0F9F0>
But not everyone understands metaclasses, so you might want to avoid them to prevent any confusion. They can be useful for stuff like this, but as you can see from the other answers, there are plenty other ways to do it.
The type(name, bases, dict)
built-in function is the correct way to dynamically construct classes--especially when given strings for class names. See the documentation here: http://docs.python.org/library/functions.html#type
In you particular example, it might look like this:
>>> def init(self):
... print 'Hello'
...
>>> Foo = type('Foo', (object,), {'__init__': init})
>>> foo = Foo()
Hello
You could use inspect to create the class map:
def get_classes():
classes = {}
for name, obj in inspect.getmembers(sys.modules[__name__]):
if inspect.isclass(obj):
classes[name] = obj
return classes
Then instantiate a class
>>> classes = get_classes()
>>> c = classes['ClassName']
>>> c
<class ClassName...>
Mostly nicked from How can I get a list of all classes within current module in Python?
In response to your update: The only way to do this is with a mapping system. If you don't want to use an explicit mapping system, then you can use one of Python's built-in mapping systems, though it's much less nice and explicit in my opinion.
obj = globals()[parser['name']]()
will access the global object of with name parser['name'] == 'foo'
. If this happens to be a class (or a class that you actually want instantiated based on user input), then you should be good to go. Otherwise, you will have to build logic around it to whitelist the classes that you actually want.
If the classes are coming from a module, then you can use that module's __dict__
attribute to the same effect.
obj = somemodule.__dict__[parser['name']]()
The same caveats apply to this situation as the previous one. It's really better to just use an explicit mapping
I would use a map of class name to class object like everyone else is saying. You can initialize it using statements like parser[foo.__name__] = foo
. If you do not want to use a mapping object, then you will have to fall back to using the eval
function like the following:
>>> class foo:
... pass
...
>>> klass_name = 'foo'
>>> klass_inst = eval(klass_name)
>>> klass_inst
<class __main__.foo at 0x1004b2b90>
>>> inst = klass_inst()
>>> inst
<__main__.foo instance at 0x1004d2680>
>>>
Of course if you want to use classes that are embedded within a package, then the package will have to be imported before you do the eval
. You really should build a mapping object so that you can limit the classes that can be accessed using this code.
精彩评论