How to Dynamically Load a Python Class

How to dynamically load a Python class

From the python documentation, here's the function you want:

def my_import(name):
components = name.split('.')
mod = __import__(components[0])
for comp in components[1:]:
mod = getattr(mod, comp)
return mod

The reason a simple __import__ won't work is because any import of anything past the first dot in a package string is an attribute of the module you're importing. Thus, something like this won't work:

__import__('foo.bar.baz.qux')

You'd have to call the above function like so:

my_import('foo.bar.baz.qux')

Or in the case of your example:

klass = my_import('my_package.my_module.my_class')
some_object = klass()

EDIT: I was a bit off on this. What you're basically wanting to do is this:

from my_package.my_module import my_class

The above function is only necessary if you have a empty fromlist. Thus, the appropriate call would be like this:

mod = __import__('my_package.my_module', fromlist=['my_class'])
klass = getattr(mod, 'my_class')

How do you dynamically load python classes from a given directory?

You are looking for pkgutil.walk_packages. Using this you can do the following:

def load(root_import_path, is_valid=lambda entity: True):
"""Returns modules in ``root_import_path`` that satisfy the ``is_valid`` test

:param root_import_path: An string name for importing (i.e. "myapp").
:param is_valid: A callable that takes a variable and returns ``True``
if it is of interest to us."""

prefix = root_import_path + u"."
modules = []

for _, name, is_pkg in walk_packages(root_import_path, prefix=prefix):
if is_pkg:
continue
module_code = __import__(name)
contents = dir(module_code)
for thing in contents:
if is_valid(thing):
modules.append(thing)

return modules

Alternatly, if you don't mind taking on a dependency, you could try the straight.plugin loader, which is a little more complicated than this simple load function.

Import class from module dynamically

It is expecting my_module to be a package containing a module named 'my_class'. If you need to import a class, or an attribute in general, dynamically, just use getattr after you import the module:

cls = getattr(import_module('my_module'), 'my_class')

Also, yes, it does only work with modules. Remember importlib.import_module is a wrapper of the internal importlib.__import__ function. It doesn't offer the same amount of functionality as the full import statement which, coupled with from, performs an attribute look-up on the imported module.

How to dynamically load class and call method in Python?

I have solved it by little real life help. I had to convert the controller cases from class to module and had to call getattr twice: Once for the module and once for the method.


Convert the controller case class into an controller case module by removing the class XYZ(): line.

From:

#!/usr/bin/env python

class dummy():
def hello(argv):
print 'Hello World'

To:

#!/usr/bin/env python

def hello(argv):
print 'Hello World'

Change the getattr code in the controller.

From:

def main(argv):
print 'Number of arguments:', len(sys.argv), 'arguments.'
print 'Argument List:', str(sys.argv)
print 'Load class: ', str(parsed_args.load_class)
print 'Call method: ', str(parsed_args.call_method)
getattr(ControllerCases[parsed_args.load_class], parsed_args.call_method)(argv)

if __name__ == "__main__":
main(sys.argv[1:])

To:

def main(argv):
print 'Number of arguments:', len(sys.argv), 'arguments.'
print 'Argument List:', str(sys.argv)
print 'Load class: ', str(parsed_args.load_class)
print 'Call method: ', str(parsed_args.call_method)
load_class = getattr(ControllerCases, parsed_args.load_class)
call_method = getattr(load_class, parsed_args.call_method)
try:
call_method(argv)
except KeyboardInterrupt:
print 'UserAborted'

if __name__ == "__main__":
main(sys.argv[1:])

Dynamic instantiation from string name of a class in dynamically imported module?

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_()

Import class dynamically in Python

From the documentation on imp.new_module, the returned module is empty. Meaning that it will never contain your class.

Perhaps what you want to do is add your target directory to sys.path and use __import__ to dynamically import those modules, then check for your class?


The following code works for me:

modules = ['foo','bar']
for mod in modules:
try:
x = reload(__import__(mod))
except ImportError:
print "bargh! import error!"
continue
try:
cls = getattr(x,'qux')
except AttributeError:
continue

a = cls()
print a.__class__.__name__

Where foo.py and bar.py are in the same directory:

#foo.py
class foo(object):
pass

and:

#bar.py
class qux(object):
pass

Instancing classes from dynamically loaded modules

After a lot of messing around, I found that the solution was pretty simple.

I only had to change the way the module gets loaded into a spec like so:

loader = importlib.machinery.SourceFileLoader(filename, dirpath+"/"+filename)
spec = importlib.util.spec_from_loader(loader.name, loader)
module = importlib.util.module_from_spec(spec)
loader.exec_module(module)

This approached lead to the inspect.getmembers to return valid classes from the module.

Dynamically load module with Inheritance

You have to import the module itself, then get its class member. You can't just import the class. Assuming your subclass is in a file accessible from the pythonpath as 'animal':

mod = __import__('animal')
spot = mod.Dog(5)

When you import a module, the interpreter first looks to see if a module with that name exists in sys.modules, then if it fails to find it there, it searches over the pythonpath looking for a package or module matching the given name. If and when it finds one, it parses the code therein, builds a module object out of it, places it on sys.modules, and returns the module object to the calling scope to be bound to the name it was imported with in the given namespace. All the items in the module (classes, variables, functions) in the module scope (not nested inside something else in the code) are then available as members of that module instance.

Edit:

In response to your comment, the real problem is that you are trying to look up an attribute of the module dynamically, not that you are trying to import anything dynamically. The most direct way to do that would be:

import sub_animal
getattr(sub_animal, 'Dog')

However, if you are trying to dynamically determine the class to initialize based upon some conditions, you probably want to read up on the factory pattern, and possibly decorators or even metaclasses, so that you can dynamically add subclasses automatically to the factory.

class AnimalFactory(type):

animal_classes = {}

def __new__(cls, name, bases, attrs):

new_class = super(AnimalFactory, cls).__new__(cls, name, bases, attrs)
AnimalFactory.animal_classes[name] = new_class
return new_class

@classmethod
def build(cls, name, *args, **kwargs):

try:
klass = cls.animal_classes[name]
except KeyError:
raise ValueError('No known animal %s' % name)
return klass(*args, **kwargs)

class Animal(object):

__metaclass__ = AnimalFactory

def __init__(self, age):

self.age = age

def speak(self):

raise NotImplementedError()

# As long as the file it is implemented in is imported at some point,
# the following can be anywhere

class Dog(Animal):

def speak(self):

return 'woof'

# And then to use, again, anywhere

new_animal = AnimalFactory.build('Dog', 5)


Related Topics



Leave a reply



Submit