views:

3185

answers:

6

I'm using a builder pattern to seperate a bunch of different configuration possibilities. Basically, I have a bunch of classes that are named an ID (something like ID12345). These all inherit from the base builder class. In my script, I need to instantiate an instance for each class (about 50) every time this app runs. So, I'm trying to see if instead of doing something like this:

ProcessDirector = ProcessDirector()
ID12345 = ID12345()
ID01234 = ID01234()

ProcessDirector.construct(ID12345)
ProcessDirector.construct(ID01234)

ID12345.run()
ID01234.run()

Can I do something like this (I know this doesn't work):

IDS = ["ID12345", "ID01234"]

ProcessDirector = ProcessDirector()
for id in IDS:
  builder = id() #some how instantiate class from string
  ProcessDirector.construct(builder)
  builder.run()

That way, when I need to add a new one in the future, all I have to do is add the id to the IDS list, rather than peppering the new ID throughout the code.

EDIT

Looks like there are some different opinions based on where the data is coming from. These IDs are entered in a file that no one else has access to. I'm not reading the strings from the command line, and I'd like to be able to do as little alteration when adding a new ID in the future.

+8  A: 

Look at this answer.

jcrossley3
A: 

There's some stuff missing from your question, so I'm forced to guess at the omitted stuff. Feel free to edit your question to correct the omissions.

class ProcessDirector( object ):
    # does something

class ID12345( SomeKindOfProcess ):
    pass

class ID001234( SomeKindOfProcess ):
    pass

idList= [ID12345, ID01234]

theProcessDirector = ProcessDirector()
for id in idList:
  builder = id() #Instantiate an object from the class object
  theProcessDirector.construct(builder)
  builder.run()

This works very nicely. It doesn't instantiate from a string -- in practice you don't often want this. Sometimes, but rarely. More commonly, you a list of class objects from which you want instance objects.

If you actually are getting your class names from the command line, then you'd make the following small change.

validClasses = [ ID12345, ID01234 ]
validMap = dict( ( (c.__name__, c) for c in validClasses ) )
nameStrings = [ "ID12345", "ID01234" ] # from your command-line 
idList= [ validMap[s] for s in nameStrings ]

Everything else remains the same.

[Also, if possible, try to start instance variable names with lowercase letters. Names which start with Uppercase Letters are usually class names.]

Edit

Removed eval. In spite of the fact that eval() is absolutely not a security hole. Eval (and exec and execfile) are only a problem if someone specifically grants access to malicious users.

S.Lott
+6  A: 

Never use eval() if you can help it. Python has so many better options (dispatch dictionary, getattr(), etc.) that you should never have to use the security hole known as eval().

Ignacio Vazquez-Abrams
-1: "never" is too strong. It's not a security hole unless you also have malicious users with access to configuration files. Preventing malicious users from touching config files is easy.
S.Lott
I read "never" to mean "avoid at all costs unless you're absolutely certain its what you need AND you know what you're doing". Using the word "never" is a good short alternative though, as it scares those who don't know what they're doing away. Those who do, will know when to ignore this anyway.
Dan
+2  A: 

Simplest way is to just create a dict.

class A(object): 
    pass
class B(object): 
    pass

namedclass = {'ID12345': A, 'ID2': A, 'B': B, 'AnotherB': B,  'ID01234': B}

Then use it (your code example):

IDS = ["ID12345", "ID01234"]

ProcessDirector = ProcessDirector()
for id in IDS:
    builder = namedclass[id]() 
    ProcessDirector.construct(builder)
    builder.run()
nosklo
+2  A: 

If you wanted to avoid an eval(), you could just do:

id = "1234asdf"
constructor = globals()[id]
instance = constructor()

Provided that the class is defined in (or imported into) your current scope.

GoldenBoy
+4  A: 

Not totally sure this is what you want, but it seems like a more Python'y way to instantiate a bunch of classes listed in a string:

class idClasses:
    class ID12345:pass
    class ID01234:pass
# could also be: import idClasses

class ProcessDirector:
    def __init__(self): self.allClasses = []
    def construct(self, builderName):
        targetClass = getattr(idClasses, builderName)
        instance = targetClass()
        self.allClasses.append(instance)

IDS = ["ID12345", "ID01234"]

director = ProcessDirector()
for id in IDS:
    director.construct(id)

print director.allClasses
# [<__main__.ID12345 instance at 0x7d850>, <__main__.ID01234 instance at 0x7d918>]
dbr