11

Trying to instantiate a class based on a string value and... 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..

Wells
  • 10,415
  • 14
  • 55
  • 85

8 Answers8

16
classmap = {
  'foo': foo
}

obj = classmap[parser['name']]()
Ignacio Vazquez-Abrams
  • 776,304
  • 153
  • 1,341
  • 1,358
15

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)()
Community
  • 1
  • 1
laurentoget
  • 176
  • 1
  • 2
7

Don't use strings:

parser = {}

class foo:
    pass


parser['foo'] = foo

obj = parser['foo']()
Falmarri
  • 47,727
  • 41
  • 151
  • 191
6

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.

Cam
  • 14,930
  • 16
  • 77
  • 128
Jochen Ritzel
  • 104,512
  • 31
  • 200
  • 194
4

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
  • the problem isn't to dynamically construct classes but to dynamically instantiate them. – aaronasterling Dec 22 '10 at 21:34
  • Then `obj = type('Foo', (object,), {'__init__': init})()` ought to do the trick. That prints 'Hello' and make obj an instance of the class. – Brian Riley Dec 22 '10 at 21:41
  • Doing it this way creates a new class for each instance. It also takes what's a nice, semantic class definition and breaks it up into functions spread around the file. Also, how do you map different functions to their respective classes. What if there was a class 'Bar' with an init that prints 'Goodbye'? – aaronasterling Dec 22 '10 at 22:04
  • +1. Although this isn't properly an answer to the question, it's what I was looking to do. (That is, the answer itself, not the silly follow-up comment.) – Grault Jun 24 '13 at 05:44
2

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?

Community
  • 1
  • 1
hajamie
  • 2,848
  • 2
  • 22
  • 20
-1

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

aaronasterling
  • 68,820
  • 20
  • 127
  • 125
-4

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.

D.Shawley
  • 58,213
  • 10
  • 98
  • 113
  • There's no need to even mentioning `eval` when there is a solution a thousand times cleaner and safer available. (I didn't downvote though) –  Dec 22 '10 at 20:25
  • Technically, when using `eval`, you are using a mapping system: `globals()`. – aaronasterling Dec 22 '10 at 21:02
  • @delnan: that is why I said that `eval` is the fall back if you don't want to use an explicit mapping object. I wanted to mention it since some people actually prefer using it. Especially people that come from Perl and Shell. Thanks for restraining the downvote though ;) – D.Shawley Dec 24 '10 at 14:12
  • @aaronasterling: I should have said _explicit mapping_ since most people don't think of variable lookup within an interpreter as a mapping even though it is. – D.Shawley Dec 24 '10 at 14:13