调度器 Python

0 投票
2 回答
645 浏览
提问于 2025-04-15 11:08

大家好,我有一个“错误”的调度器:

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 个回答

1

你真的需要把东西当作模块导入吗?如果你只是从文件系统的任意位置加载代码,那么与其去调整模块路径什么的,不如直接使用 execfile

也就是说:

for file in glob.glob('modules/commands/Command*.py'):
    if os.path.isdir(file):
        continue

    moddict={}
    execfile(file, moddict)
    _cmdClass = moddict['Command']
    ...
1

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']

可能会重复文件名一次,但比起一堆魔法,这样简单明了且更稳健。

撰写回答