从字符串动态导入文件中的方法


80

我有一个字符串,说:abc.def.ghi.jkl.myfile.mymethod。如何动态导入mymethod

这是我的处理方法:

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

我想知道是否完全需要导入单个模块。

编辑:我正在使用Python版本2.6.5。

Answers:


108

在Python 2.7中,您可以使用importlib.import_module()函数。您可以使用以下代码导入模块并访问其中定义的对象:

from importlib import import_module

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

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

met()

2
值得注意的是,Python文档建议使用importlib.import_module()over __import__()docs.python.org/2/library/functions.html#__import__-适用于2.7+。
BoltzmannBrain 2015年

4
import_module + rsplit = 唯一的方法。
塞西尔·库里

32

您不需要导入各个模块。导入要从中导入名称的模块并提供fromlist参数就足够了:

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

对于您的示例abc.def.ghi.jkl.myfile.mymethod,将此函数称为

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

(请注意,模块级函数在Python中称为函数,而不是方法。)

对于这样简单的任务,使用importlib模块没有任何优势。


import_from()为我指明了正确的方向。myClass = getattr(__import__("module.to.import", fromlist=["myClassName"]), "myClassName")。谢谢您的帮助!
Fydo 2014年

1
__import__方法确实有效。但是尝试跑步help(__import__)。它说:“因为此函数供Python解释器使用,而不是一般用途,所以最好importlib.import_module()以编程方式导入模块。”
twasbrillig 2015年

5
@twasbrillig:当我回答这个问题时,Python 2.5和2.6仍在广泛使用。今天,当然最好使用它importlib
Sven Marnach

1
太好了,谢谢。更新答案以反映这一点可能是一个好主意。
twasbrillig 2015年

26

对于Python <2.7,可以使用内置方法__ import__

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

对于Python> = 2.7或3.1,已添加了便捷方法importlib.import_module。像这样导入模块:

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

更新:根据评论更新了版本(我必须承认,直到最后我都没有阅读要导入的字符串,而且我错过了应该导入模块方法而不是模块本身的事实)

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

6
这两个解决方案都不起作用,因为第一个参数必须是和中都必须是模块名称__import__()importlib.import_module()
克里斯·哈迪

1
importlib.import_module('abc.def.ghi.jkl.myfile.mymethod')将不起作用,因为您必须指定“以绝对或相对术语导入的模块”,而不是方法的“限定”名称。
frm 2012年

显然,这不起作用,因为要导入的第一个参数应该是模块,而不是字符串。
Lakshman Prasad 2012年

11

目前尚不清楚您要对本地名称空间执行的操作。我假设您只想my_method输入本地文字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()

虽然仅添加my_method到本地名称空间,但确实加载了模块链。您可以通过查看sys.modules导入前后的键来查看更改。我希望这比您的其他答案更清楚,更准确。

为了完整起见,这就是您添加整个链的方式。

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

最后,如果您正在使用__import__()并且在程序启动后修改了搜索路径,则可能需要使用__import__(normal args, globals=globals(), locals=locals())。为什么要进行复杂的讨论。


the_module和的第一个示例same_module是错误的,会产生不同的结果。投票失败。
sleepycal

7
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 

2
感谢您的回答,这只是一个小问题:.strip()在您所说的情况下,我认为行为不正确。具体来说,如果文件以“ py”开头,这些字符也将被删除。例如,"pyfile.py".strip(".py")Produces "file""pyfile"在这种情况下最好产生它。name.replace(".py","")工作正常,但是。
jxmorris12

1

这个网站有一个很好的解决方案:load_class。我这样使用它:

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

根据要求,以下是Web链接中的代码:

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)

请提供给定链接中的摘要/代码。仅链接的答案不是很好,因为它们易于造成链接腐烂。
另一个用户


0

我倾向于这样做的方式(以及其他一些库,例如pylons和粘贴,如果我的记忆正确的话)是通过在它们之间使用':'来将模块名称与功能/属性名称分开。请参见以下示例:

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

这使得import_from(path)下面的功能更易于使用。

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

1
为什么要这样做呢?这要求调用者使用冒号将模块名和属性名连接在一起,并且该函数需要在冒号处再次拆分。为什么不首先使用两个参数呢?
Sven Marnach 2012年

1
通常,出现这种情况的情况是,您使用配置文件(.yaml,.ini等)将回调从框架连接到应用程序。然后,您可以使用配置文件中的单个字符串来指定框架应调用的函数等。
克里斯·哈迪

好吧,是的,您可能希望选择它作为配置文件语法。但这与OP的问题有何关系?(即使我选择了它作为配置文件语法,我也希望由配置文件解析器而不是随机API函数来解析它。)
Sven Marnach 2012年

-1

这个怎么样 :

def import_module(name):

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

不鼓励使用魔术方法。请改用importlib。
dephinera

您能详细说明为什么它令人沮丧吗?@dephinera
Aymen Alsaadi

好吧,因为魔术方法旨在由解释器调用,而不是由我们的代码显式调用。通过明确地调用它们,我们相信它们的签名将永远不会改变,但是在将来的语言版本中可能会改变。
dephinera
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.