ChatGPT解决这个技术问题 Extra ChatGPT

Dynamically import a method in a file, from a string

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.


f
frm

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

Worth noting the Python docs recommend using importlib.import_module() over __import__(): docs.python.org/2/library/functions.html#__import__ -- for 2.7+.
import_module + rsplit = The One True Way.
S
Sven Marnach

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.


import_from() pointed me in the right direction. myClass = getattr(__import__("module.to.import", fromlist=["myClassName"]), "myClassName"). Thanks for the help!
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."
@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.
Cool, thanks. Might be a good idea to update the answer to reflect this.
g
gecco

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")

Neither of these solutions will not work because the first parameter must be a module name in both __import__() and importlib.import_module().
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.
This does not work, obviously, as the first parameter to import should be a module, not a string.
C
Cabbage soup
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 

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.
O
Ondrej Slinták

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.


Your first example of the_module and same_module is wrong and produce different results. Downvoting.
t
the-typist

This website has a nice solution: load_class. I use it like this:

foo = load_class(package.subpackage.FooClass)()
type(foo) # returns FooClass

As requested, here is the code from the web link:

import importlib

def load_class(full_class_string):
    """
    dynamically load a class from a string
    """

    class_data = full_class_string.split(".")
    module_path = ".".join(class_data[:-1])
    class_str = class_data[-1]

    module = importlib.import_module(module_path)
    # Finally, we retrieve the Class
    return getattr(module, class_str)

Please include the summary/code from the given link. Link-only answers are not great because they are prone to link rot.
D
Daniel Roseman

Use importlib (2.7+ only).


I use 2.6.5. Can I do a from __future__ thing?
No, __future__ is for language features, not new stdlib modules.
Use the buit-in __import__ as in the examples above. It works back to 2.5 and, without keywords, before that.
K
Kris Hardy

The way I tend to to this (as well as a number of other libraries, such as pylons and paste, if my memory serves me correctly) is to separate the module name from the function/attribute name by using a ':' between them. See the following example:

'abc.def.ghi.jkl.myfile:mymethod'

This makes the import_from(path) function below a little easier to use.

def import_from(path):
    """
    Import an attribute, function or class from a module.
    :attr path: A path descriptor in the form of 'pkg.module.submodule:attribute'
    :type path: str
    """
    path_parts = path.split(':')
    if len(path_parts) < 2:
        raise ImportError("path must be in the form of pkg.module.submodule:attribute")
    module = __import__(path_parts[0], fromlist=path_parts[1])
    return getattr(module, path_parts[1])


if __name__=='__main__':
    func = import_from('a.b.c.d.myfile:mymethod')
    func()

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?
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.
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.)
A
Aymen Alsaadi

How about this :

def import_module(name):

    mod = __import__(name)
    for s in name.split('.')[1:]:
        mod = getattr(mod, s)
    return mod

Using the magic method is discouraged. Use importlib instead.
Can you elaborate on why it is discouraging? @dephinera
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.
@dephinera, I did not see anything like that before. Can you reference a link. I googled it and could not find anything.