1

I am looking for a way to "identify" the class name on the fly based on an incoming parameter and create its object. The code which I attempted gives error "TypeError: 'str' object is not callable". Any suggestions on how to achieve this?

class interfaceMsg:
    def __init__(self):
        #Do some initializations


    def updateDB(self, cell_index, xml_msg):
        #Do something general


class interfaceMsg_myMsg(interfaceMsg):
    def __init__(self):
        interfaceMsg.__init__(self)
        #Do some specific initializations


class interfaceMsg_yourMsg(interfaceMsg):
    def __init__(self):
        interfaceMsg.__init__(self)
        #Do some special initializations

    def updateDB(self, cell_index, xml_msg):
        #Do something special



##and so on several derived classes



def doUpdateDB(cell_index, xml_msg):
    # I basically want to create an object on the fly and call its updateDB function

    #Create the class name correspoding to this message
    class_name = "interfaceMsg_" + xml_msg.attrib["showname"]

    #class_name becomes interfaceMsg_myMsg or interfaceMsg_yourMsg or interfaceMsg_xyzMsg

    #This gives TypeError: 'str' object is not callable
    obj = class_name()

    obj.updateDB(cell_index, xml_msg)

Thanks

2 Answers2

0

what i do (for example in an engine of mine Contemplate), is this:

  1. have a set of classes available in subfolder
  2. dynamicaly import the appropriate class file
  3. get an instance of the class

All these assuming that the class is dynamic but from a specific set of available classes, sample code follows:

sample available class:

def __getClass__():

    class AClass:

        # constructor

        def __init__(self):
            pass

    return AClass



# allow to 'import *'  from this file as a module

__all__ = ['__getClass__']

sample dynamic class loading:

def include( filename, classname, doReload=False ):
    # http://www.php2python.com/wiki/function.include/
    # http://docs.python.org/dev/3.0/whatsnew/3.0.html
    # http://stackoverflow.com/questions/4821104/python-dynamic-instantiation-from-string-name-of-a-class-in-dynamically-imported

    #_locals_ = {'Contemplate': Contemplate}
    #_globals_ = {'Contemplate': Contemplate}
    #if 'execfile' in globals():
    #    # Python 2.x
    #    execfile(filename, _globals_, _locals_)
    #    return _locals_[classname]
    #else:
    #    # Python 3.x
    #    exec(Contemplate.read(filename), _globals_, _locals_)
    #    return _locals_[classname]

    # http://docs.python.org/2/library/imp.html
    # http://docs.python.org/2/library/functions.html#__import__
    # http://docs.python.org/3/library/functions.html#__import__
    # http://stackoverflow.com/questions/301134/dynamic-module-import-in-python
    # http://stackoverflow.com/questions/11108628/python-dynamic-from-import
    # also: http://code.activestate.com/recipes/473888-lazy-module-imports/
    # using import instead of execfile, usually takes advantage of Python cached compiled code

    global _G
    getClass = None
    directory = _G.cacheDir
    # add the dynamic import path to sys
    os.sys.path.append(directory)
    currentcwd = os.getcwd()
    os.chdir(directory)   # change working directory so we know import will work

    if os.path.exists(filename):

        modname = filename[:-3]  # remove .py extension
        mod = __import__(modname)
        if doReload: reload(mod) # Might be out of date
        # a trick in-order to pass the Contemplate super-class in a cross-module way
        getClass = getattr( mod, '__getClass__' )

    # restore current dir
    os.chdir(currentcwd)
    # remove the dynamic import path from sys
    del os.sys.path[-1]

    # return the Class if found
    if getClass:  return getClass()
    return None

This scheme enables these things:

  1. dynamic file and module/class loading, where classname/file is dynamic but class name might not correspond to filename (have a generic __getClass__ function for this in each module/class)

  2. the dynamic class file can be generated (as source code) on the fly or be pre-cached and so on... No need to (re-)generate if already available.

  3. Set of dynamic classes is specific and not ad-hoc, although it can be dynamicaly changed and/or enlarged at run-time if needed

Nikos M.
  • 8,033
  • 4
  • 36
  • 43
  • That seems a bit overcomplicated for the problem OP described; they seem to simply need `globals` instead of your homemade import system. – l4mpi Aug 18 '15 at 13:45
  • @l4mpi, i dont think so, but i'll let OP decide, i think this solution is more clean and generic, requiring only a scheme to load and identify classes, but one might like to do it differently. As a sidenote, for the use purpose (mentioned in start of answer), this was best solution – Nikos M. Aug 18 '15 at 13:48
  • I don't doubt that there are use cases where something like this is needed, I'm just pretty certain that OPs situation isn't such a case. – l4mpi Aug 18 '15 at 14:01
  • 1
    Thanks Nikos M. That helped. Instead of following __getClass__ approach, I noticed that getattr is able to return a class which can be used to instantiate the object. mod = __import__("my_module") class_name = getattr(mod, "myMsg_interfaceMsg") obj = class_name() That resolved my problem..gracefully –  Aug 19 '15 at 08:47
  • unfortunately, webpage says that I dont have enough "reputations" to vote..but thanks again Nikos M –  Aug 19 '15 at 08:51
  • @lambdatree, ok great, the `getClass` approach enables one more thing, the dynamic class can extend other classes (not known untill import time), which can be passed to `getClass`. If this is fine for you it is great, and you can also accept the answer (the tick sign next to answer) – Nikos M. Aug 19 '15 at 13:32
  • @lambdatree, hello, i want to remind you to accept the answer if it has solved your issue and it has been unjustly downvoted. Thanks – Nikos M. Dec 24 '15 at 12:18
-1

You're probably looking for globals, which is a dictionary of all global variables (that includes toplevel classes as well as imports):

from yourClasses import ClassA, ClassC

class ClassB(object): pass

def instantiate(className):
    cls = globals()[className] #find the class in the globals dict
    instance = cls()
    return instance

a_instance = instantiate("ClassA")

import random
random_instance = instantiate("Class"+random.choice("ABC"))

Or alternatively, build your own Dictionary of classes - that would be a cleaner choice, especially when dealing with user input.

myClasses = {"A": ClassA, "B": ClassB, "SomethingElseEntirely": ClassC}
clsName = raw_input("")
cls = myClasses[clsName]
instance = cls()

This is of course not limited to classes, you could also store and call methods in the same way.

l4mpi
  • 5,103
  • 3
  • 34
  • 54