我正在编写一个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给出了应该从name给出的模块导入的对象或子模块的名称.
来源:https://docs.python.org/3/library/functions.html# 进口
所以目前我有类似的东西:
command = sys.argv[1] try: command_module = __import__("myapp.commands.%s" % command, fromlist=["myapp.commands"]) except ImportError: # Display error message command_module.run()
这很好用,我只是想知道是否可能有更惯用的方法来完成我们正在使用此代码.
请注意,我特别不想使用鸡蛋或扩展点.这不是一个开源项目,我不希望有"插件".重点是简化主应用程序代码,并在每次添加新命令模块时无需修改它.
使用早于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中扯下来.
Python 2.7和3.1及更高版本的推荐方法是使用importlib
模块:
importlib.import_module(name,package = None)
导入模块.name参数指定以绝对或相对术语导入的模块(例如pkg.mod或..mod).如果名称是以相对术语指定的,那么package参数必须设置为包的名称,该包用作解析包名的锚(例如import_module('.. mod','pkg.subpkg')将导入pkg.mod).
例如
my_module = importlib.import_module('os.path')
注意:从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
使用imp模块或更直接的__import__()
功能.
如果你想在当地人:
>>> 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()
你可以使用exec
:
exec "import myapp.commands.%s" % command
Nowadays you should use importlib.
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)
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)