How to create Class Instance named from variable?

I’d like to create some instances of a class who’s names are determined at runtime, but I don’t know the best way to do this.

A bit of pseudo code:

class SomeClass(object):
    pass

inst_names = ["tom", "dick", "emily"]

for inst_name in inst_names:
    "{inst_names}" = SomeClass()

So I should end up with 3 instances of SomeClass called tom, dick & emily.

At the moment I’m using exec() which I know is bad, in this way:

xcmd = "{} = SomeClass()".format(inst_name)
exec(xcmd)

Any suggestions?

Note: I edited your question to wrap the code with [code]…[/code] for formatting.

So yes, using a string to dynamically load python is generally bad, but it’s important to know why. If you are getting this class name from the user as input then there’s a very good chance they can type in Python code you didn’t expect and cause it to execute. Evaluating strings isn’t bad on its own. Technically every web framework on the planet does this in its templating language. What’s bad is if you eval strings that come from the user as those are nearly impossible to restrict correctly. In fact, trying to detect bad code in strings from users was proven impossible by Alan Turing, so nobody is going to be able to do that. https://en.wikipedia.org/wiki/Halting_problem

There is also the https://docs.python.org/3/library/inspect.html module, but since you’re setting variables and not necessarily just looking up a class, you might not find that too useful. It would still have the same problem as above though since, if I realize you’re setting variables to classes, I can simply find a way to make you set some important variable to whatever I want. You really are stuck in this case.

To make this work safely, you need to have these variables placed in a safe container that doesn’t actually impact your code, and the best place I can think of is a dict. In theory there’s no reason to do:

joe = Person()
joe.dostuff()

When you can also just do this:

people['joe'] = Person()
people['joe'].dostuff()

That’d be safer, works better with how you’re probably using it, turns all your interactions with these objects into data not code, which is generally easy to code against generically. For example, how do you tell if one of these objects does not exist? You can do it in Python but python generally assumes variables exist and throws errors when they don’t. Put them in a dict and you can tell right away.

The final thing is, you could possibly create a module, and that module will have a dict (look at https://docs.python.org/3/library/inspect.html again), then you can set the variables in the module and get the ability to use these as variables while also firewalling them into a safe place that can’t be modified. Still, it’s not going to be as definitely safe as using a dict, but it might be a fun exercise to see how that’d work.

Thanks Zed, I’m new to python and markdown so thanks for editing my post - I knew there had to be a better way!

I am already using a dict in my code to hold the “instance name” but I hadn’t considered that I could actualy use it to hold the instance itself as it were, which I think is what you’re suggesting? I’m still getting my head around all this so I’ll take some time to digest what you’ve said and see what works, but at worst I guess using exec (or eval?) is not quite as sinful as I had feared as I’m not taking any user input for this.

I guess the other tricky bit here is that I also don’t actually know the name of the class that I’m creating an instance from, so my code looks more like this:

devdict = {'DEVCLASS': "BasicSwitch", 'DEVINST': "F1B2L1", 'TDCODE': 22101}

xcmd = "{} = {}({})".format(devdict['DEVINST'], devdict['DEVCLASS'], devdict['TDCODE']

exec(xcmd)

So I guess I can rewrite this as:

devdict = {'DEVCLASS': "BasicSwitch", 'DEVINST': None, 'TDCODE': 22101}

devdict['DEVINST'] = BasicSwitch(devdict['TDCODE'])

But how do I replace the class name “BasicSwitch” with whatever is in devdict[‘DEVCLASS’] ?

Can I do this?

devdict['DEVINST'] = devdict['DEVCLASS'](devdict['TDCODE'])

Somehow that doesn’t look right!

You can totally do that. Python treats nearly everything like it’s a variable, so try this:

>>> class Something(object):
...     pass
... 
>>> x = Something
>>> y = x()
>>> y.__class__
<class '__main__.Something'>
>>> 

That’s brilliant! I guess it’s subtly different from my code where I have

x = "Something"

rather than

x = Something

I think that’s where I was going wrong:

>>> class Something(object):
...     pass
...
>>> x = Something
>>> y = x()
>>> y.__class__
<class '__main__.Something'>
>>>
>>> a = "Something"
>>> b = a()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'str' object is not callable
>>>

Thanks for your help Zed - great books btw, they’ve been a massive help in getting me started with Python.

For anyone else reading this in future, here’s what I’ve ended up with:

>>> class Something(object):
...     def __init__(self, tdcode):
...         self.tdcode = tdcode
...
...     def inside(self):
...         print("TDCode : ",self.tdcode)
...
>>> DEVICES = {
...     "Key 1": {
...         'DEVCLASS': Something,
...         'DEVINST': None,
...         'TDCODE': 1111
...     },
...     "Key 2": {
...         'DEVCLASS': Something,
...         'DEVINST': None,
...         'TDCODE': 2222
...     }
... }
>>>
>>> DEVICES
{'Key 1': {'DEVCLASS': <class '__main__.Something'>, 'DEVINST': None, 'TDCODE': 1111},
 'Key 2': {'DEVCLASS': <class '__main__.Something'>, 'DEVINST': None, 'TDCODE': 2222}}
>>>
>>> DEVICES['Key 1']['DEVINST'] = DEVICES['Key 1']['DEVCLASS'](DEVICES['Key 1']['TDCODE'])
>>>
>>> DEVICES['Key 1']['DEVINST'].inside()
TDCode :  1111
>>>
>>> DEVICES['Key 2']['DEVINST'] = DEVICES['Key 2']['DEVCLASS'](DEVICES['Key 2']['TDCODE'])
>>>
>>> DEVICES['Key 2']['DEVINST'].inside()
TDCode :  2222
>>>
>>>
>>> DEVICES
{'Key 1': {'DEVCLASS': <class '__main__.Something'>, 'DEVINST': <__main__.Something object at 0x0000021E6B056128>, 'TDCODE': 1111},
 'Key 2': {'DEVCLASS': <class '__main__.Something'>, 'DEVINST': <__main__.Something object at 0x0000021E6B056160>, 'TDCODE': 2222}}
>>>