我遇到这样的情况,在我的Python 3项目中,必须在运行时包含某些模块。我为此使用importlib.import_module
。
第二次更新:我确实找到了一种方法来完成我想要的事情。一些其他代码可能会使此处的某些链接略微有些偏离。我将发布答案以显示我的工作。
首次更新:通过各种Python组,有人告诉我这是不可能的。我在底部有一个更新部分。
设置
这个想法是,当用户运行我的脚本时,将搜索所有名为“ agents _ *。py”(其中*替换为某个单词的文件)的类,并将其导入。因此,例如,名为agents_human.py
的文件将具有名为HumanAgent
的类。正是将要实例化的HumanAgent
类,这就是为什么我需要导入模块(文件)以便发生这种情况的原因。
基本问题
问题有两个:
问题上下文
我有一个代码库,显示了我要执行的操作:https://github.com/jeffnyman/pacumen
问题出在load_agent()方法中;特别是this statement。
为此,如果您克隆该仓库,则可以从项目根目录运行以下命令:
python3 -m pacumen
agents_human.py
文件位于项目的根目录中,那么将起作用。agents_human.py
文件是项目中的agents
目录,它将 不 起作用。在我的代码存储库中,agents_human.py
文件位于agents
目录中,因此导入失败。但是,如果将该文件移至根目录,则可以看到导入成功。但是,从load_agent()
函数中可以看出,我已经将当前工作目录以及“ agents”目录添加到路径(lines 119 - 121)
当我说导入失败时,我的意思是:当调用导入逻辑时,将触发the ImportError
exception。同样,仅当agents_human.py
文件位于agents
目录中时。
要解决的问题
理想情况下,我希望这对两种方法都适用:文件位于根目录中以及agents
目录中。
其他问题背景
我只是不得不将文件放在根目录中。但是,更糟糕的是,在安装程序时(而不是从项目根目录运行),即使是上面可用的部分也不再起作用。
具体来说,我从克隆项目的项目根目录中执行以下操作:
pip3 install .
然后我创建一些目录(例如test_pacumen
)。在其中放置了一个agents_human.py
文件,唯一的内容是这样:
class HumanAgent:
pass
(还需要我项目中的layouts
目录。)
然后我跑:
pacumen
在这种情况下,逻辑将永远不会导入agents_human.py
文件……即使它位于执行命令的根目录下。
我尝试了什么
通过查看各种示例,我的理解是我必须通过相对名称导入或提供显式锚。但这似乎是通过提供在其中搜索模块的路径进行的。我上面链接的代码似乎就是这样做的。
我获得了在路径上找到的模块名称(通过this line),并且在所有情况下,它确实找到了我放置的相关文件。因此,似乎该路径正在起作用;不是导入,而是导入。那是我找不到很多指导的地方。
我还尝试过以这种方式获取每个文件路径的路径的结尾部分:
module_end_dir = os.path.basename(os.path.normpath(module_dir))
然后,在适当的位置,我已经尝试过:
agent_module = importlib.import_module(f"{module_end_dir}.{module_name[:-3]}")
当agents_human.py
位于agents
目录中时,该方法在本地有效,但是如果agents_human.py
位于根目录中,则该方法无效。在安装该程序后,它仍然根本无法工作。
问题摘要
在第一个问题上下文(未安装的上下文)中,我不知道为什么无法从agents_human.py
导入agents
模块,但是可以从根。
在第二个上下文(已安装)中,即使agents_human.py
模块似乎在根目录中也无法导入。
我希望我在这里提供的内容不会太混乱。
更新:
我想到了为load_agent
函数尝试类似的方法:
def load_agent(pacman, not_human):
module = importlib.import_module("agents.agents_human")
return getattr(module, pacman)
在通过项目根目录运行程序时有效,但是在通过pip安装项目时无效。显然,这与脚本的入口点有关,以及在这种情况下如何无法使Python识别目录为模块。
我提供了另一个示例,如下所示:
def load_agent(pacman, not_human):
import re
pysearchre = re.compile('.py$', re.IGNORECASE)
agent_files = filter(pysearchre.search, os.listdir(os.path.join(os.getcwd(), 'agents')))
form_module = lambda fp: '.' + os.path.splitext(fp)[0]
agents = map(form_module, agent_files)
importlib.import_module('agents')
for agent in agents:
if not agent.startswith('__'):
agent_module = importlib.import_module(agent, package="agents")
if pacman in dir(agent_module):
return getattr(agent_module, pacman)
raise Exception("The agent " + pacman + " is not specified in any agents_*.py file.")
相同的情况。无需安装即可在程序运行时使用。安装程序时不会。
基本上,这是为了确认通过pip安装Python程序时我想做的事是不可能的。我不完全相信,但是我提供了此更新,以防其他人遇到。
答案 0 :(得分:1)
您所做的操作不起作用,因为您在setup.py
中有入口点,而且还因为安装程序时Python没有agents
目录的概念。
您可以尝试将代理路径添加到sys.path:
sys.path.insert(0, os.getcwd() + '/agents')
但是我怀疑当有人安装您的程序时这是否有用。 (而且,当您从发现的项目中运行程序时,您不需要这样做。)直接从项目本身运行时,通过pip进行安装基本上会将执行脚本与目录上下文分离。
您可能要尝试的另一件事就是通过文件位置导入。换句话说,请勿将agents
目录(来自安装)视为实际模块。相反,只需将其视为是:目录位置。问题在于这是Python设法在版本之间搞砸的另一个领域。
如果您使用的是3.5及更高版本,则可以执行以下操作:
import importlib.util
spec = importlib.util.spec_from_file_location("agents.agent_human", "/agents/agents_human.py")
foo = importlib.util.module_from_spec(spec)
spec.loader.exec_module(foo)
如果您使用的是Python 3.3或3.4,则可以执行以下操作:
from importlib.machinery import SourceFileLoader
foo = SourceFileLoader("agents.agent_human", "/agents/agents_human.py").load_module()
但是请注意,即使在Python 3.4中,它也已被弃用。
您可能想尝试一些变化。假设您想尝试查找该目录中所有文件中的类,则可能必须添加一些逻辑以检查agents
目录中的任何文件。
答案 1 :(得分:0)
我找到了一个基本可行的解决方案,但是我不得不稍微修改一下要求。最初,我希望这种情况是有人可以安装我的pacumen项目并在项目根目录或项目根目录内的agents
目录中找到某些代理文件。
一个问题是,尽管这将适用于项目的本地副本,但是如果通过pip
安装了项目,则无法导入agents
目录,因为该目录未被识别。 / p>
我最后做的是接受我在项目根目录中仅包含模式为agents_*.py
的文件。我实际上可以通过这样在load_agents()
方法中增加一行来实现这一目的:
sys.path.insert(0, os.getcwd())
这里是method with the line in question。再加上我已有的代码,就可以很好地工作。