278

In python, I have to instantiate certain class, knowing its name in a string, but this class 'lives' in a dynamically imported module. An example follows:

loader-class script:

import sys
class loader:
  def __init__(self, module_name, class_name): # both args are strings
    try:
      __import__(module_name)
      modul = sys.modules[module_name]
      instance = modul.class_name() # obviously this doesn't works, here is my main problem!
    except ImportError:
       # manage import error

some-dynamically-loaded-module script:

class myName:
  # etc...

I use this arrangement to make any dynamically-loaded-module to be used by the loader-class following certain predefined behaviours in the dyn-loaded-modules...

10 Answers 10

385

You can use getattr

getattr(module, class_name)

to access the class. More complete code:

module = __import__(module_name)
class_ = getattr(module, class_name)
instance = class_()

As mentioned below, we may use importlib

import importlib
module = importlib.import_module(module_name)
class_ = getattr(module, class_name)
instance = class_()
Sign up to request clarification or add additional context in comments.

4 Comments

module = __import__(module, fromlist=[name]) only worked for me.
If anyone is having trouble with the import method Sven mentioned above, I found my code worked better using the following method instead importlib.import_module. Can be used like: module = importlib.import_module(module_name)
@jpennell you should post that as an answer, it's often more useful to be able to directly use the string returned by obj.__module__
importlib.import_module will load the .py file into a pyc if needed as well as handle the complete module.name.pathing.to.get.to.the class. __import__ will not do either of these things, in a django environment (not tested outside of this)
180

tl;dr

Import the root module with importlib.import_module and load the class by its name using getattr function:

# Standard import
import importlib
# Load "module.submodule.MyClass"
MyClass = getattr(importlib.import_module("module.submodule"), "MyClass")
# Instantiate the class (pass arguments to the constructor, if needed)
instance = MyClass()

explanations

You probably don't want to use __import__ to dynamically import a module by name, as it does not allow you to import submodules:

>>> mod = __import__("os.path")
>>> mod.join
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'module' object has no attribute 'join'

Here is what the python doc says about __import__:

Note: This is an advanced function that is not needed in everyday Python programming, unlike importlib.import_module().

Instead, use the standard importlib module to dynamically import a module by name. With getattr you can then instantiate a class by its name:

import importlib
my_module = importlib.import_module("module.submodule")
MyClass = getattr(my_module, "MyClass")
instance = MyClass()

You could also write:

import importlib
module_name, class_name = "module.submodule.MyClass".rsplit(".", 1)
MyClass = getattr(importlib.import_module(module_name), class_name)
instance = MyClass()

This code is valid in python ≥ 2.7 (including python 3).

7 Comments

maybe I don't understand your answer, but I have used import to import submodules: __import__("module." + submodule_name_string)
The following code results in an AttributeError: mod = __import__("os.path"); mod.join while the following doesnt: mod = importlib.import_module("os.path"); mod.join
oh I see, you're right but I have done the following to get the os.path.join method: getattr(sys.modules["os.path"], "join")
ha! and I see that when using that, then 'import' is unnecessary XD
The option marked as answer didn't worked for me (using submodules) however this answer does. I think it should be the answer since it's more generic solution for dynamically loading modules.
|
23

Copy-paste snippet:

import importlib
def str_to_class(module_name, class_name):
    """Return a class instance from a string reference"""
    try:
        module_ = importlib.import_module(module_name)
        try:
            class_ = getattr(module_, class_name)()
        except AttributeError:
            logging.error('Class does not exist')
    except ImportError:
        logging.error('Module does not exist')
    return class_ or None

Comments

17

Use getattr to get an attribute from a name in a string. In other words, get the instance as

instance = getattr(modul, class_name)()

Comments

16

One can simply use the pydoc.locate function.

from pydoc import locate
my_class = locate("module.submodule.myclass")
instance = my_class()

Comments

8

If you want this sentence from foo.bar import foo2 to be loaded dynamically, you should do this

foo = __import__("foo")
bar = getattr(foo,"bar")
foo2 = getattr(bar,"foo2")

instance = foo2()

Comments

7

If you want to import a class and method from string, you should do this:

dynamic_import 
│   my_class.py
│      
└───subfolder
│   │   my_subfolder_module.py
│   │

my_subfolder_module.py

 class MySubfolderClass():
        def test_method(self):
            print ("Hello World")

main.py

import importlib 
  
module = importlib.import_module('subfolder.my_subfolder_module')
class_ = getattr(module, "MySubfolderClass")
method_instance = getattr(class_(),"test_method")
method_instance()
#it will output the result of the test method, which is "Hello World"

Comments

5

I couldn't quite get there in my use case from the examples above, but Ahmad got me the closest (thank you). For those reading this in the future, here is the code that worked for me.

def get_class(fully_qualified_path, module_name, class_name, *instantiation):
    """
    Returns an instantiated class for the given string descriptors
    :param fully_qualified_path: The path to the module eg("Utilities.Printer")
    :param module_name: The module name eg("Printer")
    :param class_name: The class name eg("ScreenPrinter")
    :param instantiation: Any fields required to instantiate the class
    :return: An instance of the class
    """
    p = __import__(fully_qualified_path)
    m = getattr(p, module_name)
    c = getattr(m, class_name)
    instance = c(*instantiation)
    return instance

Comments

1

Using python globals() seems rational in this case:

# first .py
class first_1:
   inside = 'I am inside first_1 class'


# second.py
from first import *
class seconda:
   inside = 'I am inside seconda'

# third.py
from second import *

instance1 = globals()['first_1']()
print(instance1.inside)
instance2 = globals()['seconda']()
print(instance2.inside)

Out:

python3 third.py 
I am inside first_1 class
I am inside seconda

Comments

0

Use this snippet code:

def to_class(path:str):
    try:
        from pydoc import locate
        class_instance = locate(path)
    except ImportError:
        print('Module does not exist')
    return class_instance or None

Usage:

if your class name is MyClass and located in my_app.models.MyClass then:

path = "my_app.models.MyClass"
my_class = to_class(path)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.