Dynamically import a method in a file, from a string

116,477

Solution 1

From Python 2.7 you can use the importlib.import_module() function. You can import a module and access an object defined within it with the following code:

from importlib import import_module

p, m = name.rsplit('.', 1)

mod = import_module(p)
met = getattr(mod, m)

met()

Solution 2

You don't need to import the individual modules. It is enough to import the module you want to import a name from and provide the fromlist argument:

def import_from(module, name):
    module = __import__(module, fromlist=[name])
    return getattr(module, name)

For your example abc.def.ghi.jkl.myfile.mymethod, call this function as

import_from("abc.def.ghi.jkl.myfile", "mymethod")

(Note that module-level functions are called functions in Python, not methods.)

For such a simple task, there is no advantage in using the importlib module.

Solution 3

For Python < 2.7 the builtin method __ import__ can be used:

__import__('abc.def.ghi.jkl.myfile.mymethod', fromlist=[''])

For Python >= 2.7 or 3.1 the convenient method importlib.import_module has been added. Just import your module like this:

importlib.import_module('abc.def.ghi.jkl.myfile.mymethod')

Update: Updated version according to comments (I must admit I didn't read the string to be imported till the end and I missed the fact that a method of a module should be imported and not a module itself):

Python < 2.7 :

mymethod = getattr(__import__("abc.def.ghi.jkl.myfile", fromlist=["mymethod"]))

Python >= 2.7:

mymethod = getattr(importlib.import_module("abc.def.ghi.jkl.myfile"), "mymethod")

Solution 4

from importlib import import_module

name = "file.py".strip('.py')
# if Path like : "path/python/file.py" 
# use name.replaces("/",".")

imp = import_module(name)

# get Class From File.py
model = getattr(imp, "classNameImportFromFile")

NClass = model() # Class From file 

Solution 5

It's unclear what you are trying to do to your local namespace. I assume you want just my_method as a local, typing output = my_method()?

# This is equivalent to "from a.b.myfile import my_method"
the_module = importlib.import_module("a.b.myfile")
same_module = __import__("a.b.myfile")
# import_module() and __input__() only return modules
my_method = getattr(the_module, "my_method")

# or, more concisely,
my_method = getattr(__import__("a.b.myfile"), "my_method")
output = my_method()

While you only add my_method to the local namespace, you do load the chain of modules. You can look at changes by watching the keys of sys.modules before and after the import. I hope this is clearer and more accurate than your other answers.

For completeness, this is how you add the whole chain.

# This is equivalent to "import a.b.myfile"
a = __import__("a.b.myfile")
also_a = importlib.import_module("a.b.myfile")
output = a.b.myfile.my_method()

# This is equivalent to "from a.b import myfile"
myfile = __import__("a.b.myfile", fromlist="a.b")
also_myfile = importlib.import_module("a.b.myfile", "a.b")
output = myfile.my_method()

And, finally, if you are using __import__() and have modified you search path after the program started, you may need to use __import__(normal args, globals=globals(), locals=locals()). The why is a complex discussion.

Share:
116,477

Related videos on Youtube

lprsd
Author by

lprsd

I am a curious learner! You should follow me on twitter as @lprsd_

Updated on July 08, 2022

Comments

  • lprsd
    lprsd almost 2 years

    I have a string, say: abc.def.ghi.jkl.myfile.mymethod. How do I dynamically import mymethod?

    Here is how I went about it:

    def get_method_from_file(full_path):
        if len(full_path) == 1:
            return map(__import__,[full_path[0]])[0]
        return getattr(get_method_from_file(full_path[:-1]),full_path[-1])
    
    
    if __name__=='__main__':
        print get_method_from_file('abc.def.ghi.jkl.myfile.mymethod'.split('.'))
    

    I am wondering if the importing individual modules is required at all.

    Edit: I am using Python version 2.6.5.

  • lprsd
    lprsd over 12 years
    I use 2.6.5. Can I do a from __future__ thing?
  • ThiefMaster
    ThiefMaster over 12 years
    No, __future__ is for language features, not new stdlib modules.
  • Kris Hardy
    Kris Hardy over 12 years
    Neither of these solutions will not work because the first parameter must be a module name in both __import__() and importlib.import_module().
  • frm
    frm over 12 years
    importlib.import_module('abc.def.ghi.jkl.myfile.mymethod') will not work because you have to specify "what module to import in absolute or relative terms" and not the "qualified" name of the method.
  • lprsd
    lprsd over 12 years
    This does not work, obviously, as the first parameter to import should be a module, not a string.
  • Sven Marnach
    Sven Marnach over 12 years
    Why should anybdoy do this? This requires the caller to join module name and attribute name with a colon, and the function needs to split again at the colon. Why not simply use two parameters in the first place?
  • Kris Hardy
    Kris Hardy over 12 years
    Often, a situation where this will come up is if you wire callbacks from a framework to your application using configuration files (.yaml, .ini, etc.). Then, you can specify which function the framework should call, etc., with a single string in your configuration file.
  • Sven Marnach
    Sven Marnach over 12 years
    Well, yeah, you might want to choose this as a configuration file syntax. But how is this related to the OP's question? (And even if I had chosen this as my config file syntax, I'd prefer to have it parsed by my config file parser, not by random API functions.)
  • Charles Merriam
    Charles Merriam about 11 years
    Use the buit-in __import__ as in the examples above. It works back to 2.5 and, without keywords, before that.
  • Fydo
    Fydo over 10 years
    import_from() pointed me in the right direction. myClass = getattr(__import__("module.to.import", fromlist=["myClassName"]), "myClassName"). Thanks for the help!
  • sleepycal
    sleepycal almost 10 years
    Your first example of the_module and same_module is wrong and produce different results. Downvoting.
  • twasbrillig
    twasbrillig about 9 years
    The __import__ approach does work. But try running help(__import__). It says "Because this function is meant for use by the Python interpreter and not for general use it is better to use importlib.import_module() to programmatically import a module."
  • Sven Marnach
    Sven Marnach about 9 years
    @twasbrillig: When I answered this question, Python 2.5 and 2.6 were still in widespread use. Today, it's certainly best to use importlib instead.
  • twasbrillig
    twasbrillig about 9 years
    Cool, thanks. Might be a good idea to update the answer to reflect this.
  • BoltzmannBrain
    BoltzmannBrain over 8 years
    Worth noting the Python docs recommend using importlib.import_module() over __import__(): docs.python.org/2/library/functions.html#__import__ -- for 2.7+.
  • Cecil Curry
    Cecil Curry over 7 years
    import_module + rsplit = The One True Way.
  • Yet Another User
    Yet Another User almost 6 years
    Please include the summary/code from the given link. Link-only answers are not great because they are prone to link rot.
  • jxmorris12
    jxmorris12 about 4 years
    Thanks for the answer, just one small problem: I don't think .strip() behaves correctly in the case you stated. Specifically, if the file starts with "py", those characters will be removed as well. For example, "pyfile.py".strip(".py") produces "file", where it'd be preferred that it produce "pyfile" in this case. name.replace(".py","") works just fine, though.
  • dephinera
    dephinera over 3 years
    Using the magic method is discouraged. Use importlib instead.
  • Aymen Alsaadi
    Aymen Alsaadi over 3 years
    Can you elaborate on why it is discouraging? @dephinera
  • dephinera
    dephinera over 3 years
    Well because magic methods are intended to be called by the interpreter, not explicitly by our code. By calling them explicitly, we rely that their signature will never change, however it might change in a future language versions.
  • Aymen Alsaadi
    Aymen Alsaadi over 3 years
    @dephinera, I did not see anything like that before. Can you reference a link. I googled it and could not find anything.