调度器 Python
大家好,我有一个“错误”的调度器:
def _load_methods(self):
import os, sys, glob
sys.path.insert(0, 'modules\commands')
for c in glob.glob('modules\commands\Command*.py'):
if os.path.isdir(c):
continue
c = os.path.splitext(c)[0]
parts = c.split(os.path.sep )
module, name = '.'.join( parts ), parts[-1:]
module = __import__( module, globals(), locals(), name )
_cmdClass = __import__(module).Command
for method_name in list_public_methods(_cmdClass):
self._methods[method_name] = getattr(_cmdClass(), method_name)
sys.path.pop(0)
它产生了以下错误:
ImportError: 没有名为 commands.CommandAntitheft 的模块
其中 Command*.py 文件放在 modules\commands\ 文件夹里
有人能帮我吗?
一个可能的解决方案(有效哦!!!)是:
def _load_methods(self):
import os, sys, glob, imp
for file in glob.glob('modules/commands/Command*.py'):
if os.path.isdir(file):
continue
module = os.path.splitext(file)[0].rsplit(os.sep, 1)[1]
fd, filename, desc = imp.find_module(module,
['./modules/commands'])
try:
_cmdClass = imp.load_module( module, fd, filename, desc).Command
finally:
fd.close()
for method_name in list_public_methods(_cmdClass):
self._methods[method_name] = getattr(_cmdClass(), method_name)
这仍然存在 bobince 提到的所有风险(谢谢 :-)),但现在我能在“运行时”加载命令了
2 个回答
你真的需要把东西当作模块导入吗?如果你只是从文件系统的任意位置加载代码,那么与其去调整模块路径什么的,不如直接使用 execfile
。
也就是说:
for file in glob.glob('modules/commands/Command*.py'):
if os.path.isdir(file):
continue
moddict={}
execfile(file, moddict)
_cmdClass = moddict['Command']
...
sys.path.insert(0, 'modules\commands')
最好不要把相对路径放进sys.path里。如果在执行过程中当前目录发生变化,这个路径就会失效。
而且如果你是在不同的目录下运行这个脚本,它也会出问题。如果你想让路径相对于脚本的位置,可以使用file。
另外,‘\’这个符号为了安全起见应该写成‘\\’,而且其实应该使用os.path.join()
,而不是依赖Windows的路径规则。
sys.path.insert(0, os.path.abspath(os.path.join(__file__, 'modules')))
sys.path.pop(0)
这样做很危险。如果其他导入的脚本修改了sys.path(这可能会发生),你可能会把错误的路径移除。而且重新加载你自己的模块也会出问题。最好还是把路径留在那儿。
module, name = '.'.join( parts ), parts[-1:]
记住你的路径里包含了‘modules’这个部分。所以你实际上是在尝试:
import modules.commands.CommandSomething
但是因为‘modules.commands’已经在你添加的搜索路径里,你真正想要的只是:
import CommandSomething
__import__( module, globals(), locals(), name )
另外,‘fromlist’是一个列表,所以如果你真的想把‘CommandSomething’写入你的本地变量,应该写成‘[name]’。(你几乎肯定不想这样;最好把fromlist留空。)
_cmdClass = __import__(module).Command
这样是行不通的,module是一个模块对象,而__import__需要的是模块名。你已经有了模块对象,为什么不直接用“module.Command”呢?
我对这一切的反应很简单:太多魔法了。
你让自己变得过于复杂,搞得一堆潜在的问题和脆弱性,因为你在搞乱导入系统的内部。这对经验丰富的Python程序员来说都很棘手。
你几乎肯定会更好地使用普通的Python模块,明确地导入它们。硬编码命令列表其实并不麻烦;把所有命令放在一个包里,__init__.py里写:
__all__= ['ThisCommand', 'ThatCommand', 'TheOtherCommand']
可能会重复文件名一次,但比起一堆魔法,这样简单明了且更稳健。