问题:如何导入给定名称的模块为字符串?
我正在编写一个以命令作为参数的Python应用程序,例如:
$ python myapp.py command1
我希望应用程序是可扩展的,也就是说,能够添加实现新命令的新模块而不必更改主应用程序源。这棵树看起来像:
myapp/
__init__.py
commands/
__init__.py
command1.py
command2.py
foo.py
bar.py
因此,我希望该应用程序在运行时找到可用的命令模块并执行适当的命令模块。
Python定义了__import__函数,该函数采用一个字符串作为模块名称:
__import __(name,globals = None,locals = None,fromlist =(),level = 0)
该函数导入模块名称,可能使用给定的全局变量和局部变量来确定如何在程序包上下文中解释该名称。fromlist提供应从名称给定的模块中导入的对象或子模块的名称。
来源:https : //docs.python.org/3/library/functions.html# import
所以目前我有类似的东西:
command = sys.argv[1]
try:
command_module = __import__("myapp.commands.%s" % command, fromlist=["myapp.commands"])
except ImportError:
# Display error message
command_module.run()
这工作得很好,我只是想知道是否可能有一种更惯用的方式来完成我们对这段代码所做的工作。
请注意,我特别不想使用鸡蛋或延伸点。这不是一个开源项目,我不希望有“插件”。关键是简化主应用程序代码,并且每次添加新命令模块时都无需对其进行修改。
I’m writing a Python application that takes as a command as an argument, for example:
$ python myapp.py command1
I want the application to be extensible, that is, to be able to add new modules that implement new commands without having to change the main application source. The tree looks something like:
myapp/
__init__.py
commands/
__init__.py
command1.py
command2.py
foo.py
bar.py
So I want the application to find the available command modules at runtime and execute the appropriate one.
Python defines an __import__ function, which takes a string for a module name:
__import__(name, globals=None, locals=None, fromlist=(), level=0)
The function imports the module name, potentially using the given globals and locals to determine how to interpret the name in a package context. The fromlist gives the names of objects or submodules that should be imported from the module given by name.
Source: https://docs.python.org/3/library/functions.html#import
So currently I have something like:
command = sys.argv[1]
try:
command_module = __import__("myapp.commands.%s" % command, fromlist=["myapp.commands"])
except ImportError:
# Display error message
command_module.run()
This works just fine, I’m just wondering if there is possibly a more idiomatic way to accomplish what we are doing with this code.
Note that I specifically don’t want to get in to using eggs or extension points. This is not an open-source project and I don’t expect there to be “plugins”. The point is to simplify the main application code and remove the need to modify it each time a new command module is added.
回答 0
使用2.7 / 3.1之前的Python,这就是您要做的事情。
对于较新的版本,看到importlib.import_module
了Python的2和和Python 3中。
您也可以使用exec
。
或者,__import__
您可以通过执行以下操作导入模块列表:
>>> moduleNames = ['sys', 'os', 're', 'unittest']
>>> moduleNames
['sys', 'os', 're', 'unittest']
>>> modules = map(__import__, moduleNames)
直接从Dive Into Python翻录。
With Python older than 2.7/3.1, that’s pretty much how you do it.
For newer versions, see importlib.import_module
for Python 2 and and Python 3.
You can use exec
if you want to as well.
Or using __import__
you can import a list of modules by doing this:
>>> moduleNames = ['sys', 'os', 're', 'unittest']
>>> moduleNames
['sys', 'os', 're', 'unittest']
>>> modules = map(__import__, moduleNames)
Ripped straight from Dive Into Python.
回答 1
对于Python 2.7和3.1及更高版本,推荐的方法是使用importlib
模块:
importlib.import_module(名称,包=无)
导入模块。name参数以绝对或相对方式指定要导入的模块(例如pkg.mod或..mod)。如果使用相对术语指定名称,则必须将package参数设置为充当解析包名称的定位符的包的名称(例如import_module(’.. mod’,’pkg.subpkg’)将导入pkg.mod)。
例如
my_module = importlib.import_module('os.path')
The recommended way for Python 2.7 and 3.1 and later is to use importlib
module:
importlib.import_module(name, package=None)
Import a module. The name argument specifies what module to import in absolute or relative terms (e.g. either pkg.mod or ..mod). If the name is specified in relative terms, then the package argument must be set to the name of the package which is to act as the anchor for resolving the package name (e.g. import_module(‘..mod’, ‘pkg.subpkg’) will import pkg.mod).
e.g.
my_module = importlib.import_module('os.path')
回答 2
注意:自Python 3.4以来不推荐使用imp,而建议使用importlib
如前所述,imp模块为您提供了加载功能:
imp.load_source(name, path)
imp.load_compiled(name, path)
我以前用这些来执行类似的操作。
在我的情况下,我使用所需的定义方法定义了一个特定的类。加载模块后,我将检查该类是否在模块中,然后创建该类的实例,如下所示:
import imp
import os
def load_from_file(filepath):
class_inst = None
expected_class = 'MyClass'
mod_name,file_ext = os.path.splitext(os.path.split(filepath)[-1])
if file_ext.lower() == '.py':
py_mod = imp.load_source(mod_name, filepath)
elif file_ext.lower() == '.pyc':
py_mod = imp.load_compiled(mod_name, filepath)
if hasattr(py_mod, expected_class):
class_inst = getattr(py_mod, expected_class)()
return class_inst
Note: imp is deprecated since Python 3.4 in favor of importlib
As mentioned the imp module provides you loading functions:
imp.load_source(name, path)
imp.load_compiled(name, path)
I’ve used these before to perform something similar.
In my case I defined a specific class with defined methods that were required.
Once I loaded the module I would check if the class was in the module, and then create an instance of that class, something like this:
import imp
import os
def load_from_file(filepath):
class_inst = None
expected_class = 'MyClass'
mod_name,file_ext = os.path.splitext(os.path.split(filepath)[-1])
if file_ext.lower() == '.py':
py_mod = imp.load_source(mod_name, filepath)
elif file_ext.lower() == '.pyc':
py_mod = imp.load_compiled(mod_name, filepath)
if hasattr(py_mod, expected_class):
class_inst = getattr(py_mod, expected_class)()
return class_inst
回答 3
回答 4
现在,您应该使用importlib。
导入源文件
该文档实际上提供了一个配方,它像这样:
import sys
import importlib.util
file_path = 'pluginX.py'
module_name = 'pluginX'
spec = importlib.util.spec_from_file_location(module_name, file_path)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
# check if it's all there..
def bla(mod):
print(dir(mod))
bla(module)
导入包裹
实际上,在当前目录下导入包(例如pluginX/__init__.py
)非常简单:
import importlib
pkg = importlib.import_module('pluginX')
# check if it's all there..
def bla(mod):
print(dir(mod))
bla(pkg)
Nowadays you should use importlib.
Import a source file
The docs actually provide a recipe for that, and it goes like:
import sys
import importlib.util
file_path = 'pluginX.py'
module_name = 'pluginX'
spec = importlib.util.spec_from_file_location(module_name, file_path)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
# check if it's all there..
def bla(mod):
print(dir(mod))
bla(module)
Import a package
Importing a package (e.g., pluginX/__init__.py
) under your current dir is actually straightforward:
import importlib
pkg = importlib.import_module('pluginX')
# check if it's all there..
def bla(mod):
print(dir(mod))
bla(pkg)
回答 5
如果您想在本地人中使用它:
>>> mod = 'sys'
>>> locals()['my_module'] = __import__(mod)
>>> my_module.version
'2.6.6 (r266:84297, Aug 24 2010, 18:46:32) [MSC v.1500 32 bit (Intel)]'
同样可以使用 globals()
If you want it in your locals:
>>> mod = 'sys'
>>> locals()['my_module'] = __import__(mod)
>>> my_module.version
'2.6.6 (r266:84297, Aug 24 2010, 18:46:32) [MSC v.1500 32 bit (Intel)]'
same would work with globals()
回答 6
您可以使用exec
:
exec("import myapp.commands.%s" % command)
You can use exec
:
exec("import myapp.commands.%s" % command)
回答 7
与@monkut的解决方案类似,但在http://stamat.wordpress.com/dynamic-module-import-in-python/中描述了可重用和容错的功能:
import os
import imp
def importFromURI(uri, absl):
mod = None
if not absl:
uri = os.path.normpath(os.path.join(os.path.dirname(__file__), uri))
path, fname = os.path.split(uri)
mname, ext = os.path.splitext(fname)
if os.path.exists(os.path.join(path,mname)+'.pyc'):
try:
return imp.load_compiled(mname, uri)
except:
pass
if os.path.exists(os.path.join(path,mname)+'.py'):
try:
return imp.load_source(mname, uri)
except:
pass
return mod
Similar as @monkut ‘s solution but reusable and error tolerant described here http://stamat.wordpress.com/dynamic-module-import-in-python/:
import os
import imp
def importFromURI(uri, absl):
mod = None
if not absl:
uri = os.path.normpath(os.path.join(os.path.dirname(__file__), uri))
path, fname = os.path.split(uri)
mname, ext = os.path.splitext(fname)
if os.path.exists(os.path.join(path,mname)+'.pyc'):
try:
return imp.load_compiled(mname, uri)
except:
pass
if os.path.exists(os.path.join(path,mname)+'.py'):
try:
return imp.load_source(mname, uri)
except:
pass
return mod
回答 8
以下内容对我有用:
>>>import imp;
>>>fp, pathname, description = imp.find_module("/home/test_module");
>>>test_module = imp.load_module("test_module", fp, pathname, description);
>>>print test_module.print_hello();
如果要导入shell脚本:
python -c '<above entire code in one line>'
The below piece worked for me:
>>>import imp;
>>>fp, pathname, description = imp.find_module("/home/test_module");
>>>test_module = imp.load_module("test_module", fp, pathname, description);
>>>print test_module.print_hello();
if you want to import in shell-script:
python -c '<above entire code in one line>'
回答 9
例如,我的模块名称类似于jan_module
/ feb_module
/ mar_module
。
month = 'feb'
exec 'from %s_module import *'%(month)
For example, my module names are like jan_module
/feb_module
/mar_module
.
month = 'feb'
exec 'from %s_module import *'%(month)
回答 10
以下为我工作:
import sys, glob
sys.path.append('/home/marc/python/importtest/modus')
fl = glob.glob('modus/*.py')
modulist = []
adapters=[]
for i in range(len(fl)):
fl[i] = fl[i].split('/')[1]
fl[i] = fl[i][0:(len(fl[i])-3)]
modulist.append(getattr(__import__(fl[i]),fl[i]))
adapters.append(modulist[i]())
它从文件夹“ modus”加载模块。模块具有与模块名称相同名称的单个类。例如,文件modus / modu1.py包含:
class modu1():
def __init__(self):
self.x=1
print self.x
结果是动态加载的类“适配器”的列表。
The following worked for me:
import sys, glob
sys.path.append('/home/marc/python/importtest/modus')
fl = glob.glob('modus/*.py')
modulist = []
adapters=[]
for i in range(len(fl)):
fl[i] = fl[i].split('/')[1]
fl[i] = fl[i][0:(len(fl[i])-3)]
modulist.append(getattr(__import__(fl[i]),fl[i]))
adapters.append(modulist[i]())
It loads modules from the folder ‘modus’. The modules have a single class with the same name as the module name. E.g. the file modus/modu1.py contains:
class modu1():
def __init__(self):
self.x=1
print self.x
The result is a list of dynamically loaded classes “adapters”.