我正在编写一个Python应用程序,它将命令作为参数,例如:
$ python myapp.py command1
Run Code Online (Sandbox Code Playgroud)
我希望应用程序是可扩展的,即能够添加实现新命令的新模块,而无需更改主应用程序源.树看起来像:
myapp/
__init__.py
commands/
__init__.py
command1.py
command2.py
foo.py
bar.py
Run Code Online (Sandbox Code Playgroud)
所以我希望应用程序在运行时找到可用的命令模块并执行适当的命令模块.
Python定义了一个__import__函数,它接受一个模块名称的字符串:
__import __(name,globals = None,locals = None,fromlist =(),level = 0)
该函数导入模块名称,可能使用给定的全局变量和局部变量来确定如何解释包上下文中的名称.fromlist给出了应该从name给出的模块导入的对象或子模块的名称.
所以目前我有类似的东西:
command = sys.argv[1]
try:
command_module = __import__("myapp.commands.%s" % command, fromlist=["myapp.commands"])
except ImportError:
# Display error message
command_module.run()
Run Code Online (Sandbox Code Playgroud)
这很好用,我只是想知道是否可能有更惯用的方法来完成我们正在使用此代码.
请注意,我特别不想使用鸡蛋或扩展点.这不是一个开源项目,我不希望有"插件".重点是简化主应用程序代码,并在每次添加新命令模块时无需修改它.
我正在尝试确定函数的实际当前模块(如果从其他地方导入,则会看到),即使当前模块是" 顶级脚本环境 " __main__.
这可能听起来像一个奇怪的事情,但背景是我需要序列化一个函数并在不同的机器上反序列化它(包括参数),为此我需要确保__main__在反序列化之前导入正确的模块AND NOT (否则我得到一个错误说AttributeError: 'module' object has no attribute my_fun).
到目前为止,我已经尝试过检查:
import inspect
print inspect.getmodule(my_fun)
Run Code Online (Sandbox Code Playgroud)
这给了我
<module '__main__' from 'example.py'>
Run Code Online (Sandbox Code Playgroud)
当然.我也试过找一些有用的东西globals(),没有运气.
我真正想要的是<module 'example' from 'example.py'>.我想一个hacky方法是使用类似的东西从文件名解析它
m_name = __main__.__file__.split("/")[-1].replace(".pyc","")
Run Code Online (Sandbox Code Playgroud)
然后按名称查找模块sys.modules[m_name].
有更干净/更好的方法吗?
编辑:在了解了ipython的"FakeModule"以及更多谷歌搜索后,我发现了这篇文章,它正好描述了我面临的问题,包括我当前的解决方案(明确导入当前模块import current_module并序列化current_module.my_fun而不是my_fun).我试图避免这种情况,因为对我的软件包的用户来说可能不太直观.
所以我试图改变一堆"来自x import x"的语句,看起来像这样:
from class_foo import class_foo
Run Code Online (Sandbox Code Playgroud)
变成动态的东西.我正在尝试将路径传递到目录并让它导入其中的所有模块.
def dynamicImport(dirPath):
filez = os.listdir(dirPath)
for file in filez:
if "class" in file:
oname = file[:-3] #cut off the file extension, trivial
imp_statement = "from " + oname + " import " + oname
#when I print imp_statement, I can verify it's being concatenated correctly
exec(imp_statement)
Run Code Online (Sandbox Code Playgroud)
当我运行此函数并将其传递给路径时,语句字符串正在正确创建并且不会产生错误,但稍后我将尝试访问其中一个导入的对象,并且会发生以下情况:
foo = class_foo()
NameError: name 'class_foo' is not defined
Run Code Online (Sandbox Code Playgroud)
显然,我做错了什么.任何帮助,将不胜感激.
我正在尝试动态加载我创建的模块.
现在这可以正常工作:
import structures.index
Run Code Online (Sandbox Code Playgroud)
但如果我通过动态导入它来尝试同样的事情,它就会失败.
struct = __import__("structures.index")
Run Code Online (Sandbox Code Playgroud)
提供的错误是:
Error ('No module named structures.index',)
Run Code Online (Sandbox Code Playgroud)
有什么想法吗?
编辑:使用完整范围时(它有用吗?):
struct = __import__("neoform.structures.index")
Run Code Online (Sandbox Code Playgroud)
这不会引发任何错误,但是,它没有加载索引模块,而是加载"neoform"模块.
"struct"的结果是:
<module 'neoform' from '/neoform/__init__.py'>
Run Code Online (Sandbox Code Playgroud)
另外,作为一个附带问题,我如何在动态加载的模块中实例化一个类?(假设所有模块都包含一个公共类名).
编辑:解决方案:(感谢coonj和Rick)这最终成功了.不知道为什么(还),但fromlist必须是"任何东西显然,因为当我把字母"a"作为一个值时它起作用(奇怪的是,假设文件中只有1个类).
def get_struct_module(self, name):
try:
return = __import__("neoform.structures." + name, fromlist='*')
except ImportError, e:
self.out.add("Could not load struct: neoform.structure." + name + "\n\n" + "Error " + str(e.args))
Run Code Online (Sandbox Code Playgroud) 我正在尝试捕获无法加载模块时发生的任何异常.目前的结果是"except"块没有被执行.
import sys
def loadModule(module):
try:
import module
except:
print """
Cannot load %s
For this script you will need:
cx_Oracle: http://cx-oracle.sourceforge.net/
pycrypto: https://www.dlitz.net/software/pycrypto/
paramiko: http://www.lag.net/paramiko/
""" % module
sys.exit(1)
loadModule(cx_Oracle)
Run Code Online (Sandbox Code Playgroud)
错误:
Traceback (most recent call last):
File "./temp_script.py", line 16, in <module>
loadModule(cx_Oracle)
NameError: name 'cx_Oracle' is not defined
Run Code Online (Sandbox Code Playgroud) 我有一个包含文件名和类名的字典如何导入这个类名,我该如何创建这个类?
例:
classNames = { 'MCTest':MCTestClass}
Run Code Online (Sandbox Code Playgroud)
我想导入MCTest并创建MCTestClass.