【问题标题】:Using exec to load module variables, using a file path使用 exec 加载模块变量,使用文件路径
【发布时间】:2018-02-27 15:52:00
【问题描述】:

一本教科书建议我应该能够做到这一点:

d = {}
exec("C://Users//Dave//Desktop//Bot//bot_config_data.py", globals(), d)
File "<string>", line 1   C://Users//Dave//Desktop//Bot//bot_config_data.py
                           ^
SyntaxError: invalid syntax

我可以这样做:

d = {}
exec('from bot_config_data import price_data', globals(), d)

但是,我想做前者。

我正在尝试编写一种方法来覆盖来自各种文件的配置数据。

我完全不在这儿吗?

更新 这本书非常具有误导性。它发布了部分代码,完整代码块的结果,然后给出了其余部分。当我参考时,我没有从头到尾地工作,而是绊倒了自己。

这是我现在拥有的代码:

data = {}
file = 'C:\\Users\\Dave\\Desktop\\Bot\\bot_config_data.py'
with open(file) as f:
    code = compile(f.read(), file, "exec")
    exec(code, globals(), data)
price_data = data["price_data"]

更新2 使用下面疯狂物理学家的回答,我的代码是:

from importlib.util import spec_from_loader, module_from_spec
from importlib.machinery import SourceFileLoader

filepath = 'C:\\Users\\Dave\\Desktop\\Bot\\bot_config_data.py'
module_data = os.path.basename(filepath)
spec = spec_from_loader(module_data, SourceFileLoader(module_data, filepath))
bot_config_data = module_from_spec(spec)
spec.loader.exec_module(bot_config_data)
price_data = bot_config_data.price_data

【问题讨论】:

  • “但是,我想做前者。” 为什么?导入语句有什么问题?
  • @Aran-Fey。这不是一个好方法,尽管使用exec 是这里的真正问题。
  • @Dave。我怀疑你的教科书不是很好。是哪一个?
  • Steven F.Lott 的“掌握面向对象编程”。我怀疑这个错误可能是我的一部分。我会仔细检查。
  • 我认为你需要去掉扩展,你可能不需要比 load_module 更复杂的东西了。

标签: python


【解决方案1】:

你完全偏离了基础,教科书给了你一些非常糟糕的建议。

exec 运行 Python 代码。与 Python 语句一样,不是文件名。来自文档:

exec(object[, globals[, locals]])

此函数支持 Python 代码的动态执行。 object 必须是字符串或代码对象。如果是字符串,则将字符串解析为一组 Python 语句,然后执行(除非出现语法错误)

这就是为什么您的第二个语句可以正常工作的原因。如果要运行 Python 模块,请导入它。如果需要使用动态名称而不是硬编码的import 语句进行导入,可以使用importlib.import_module

price_data = importlib.import_module('bot_config_data').price_data

这将为您运行整个导入机制,包括确保 bot_config_datasys.modules 结尾。

如果您真的需要更高级的东西,您可以使用__import__ 机器。 __import__import 语句的底层实现:

d = {}
bot_config_data = __import__('bot_config_data', locals=d, from_list=['price_data'])
price_data = bot_config_data.price_data

如果您想完全控制流程,可以使用此处描述的低级机制:How to import a module given the full path?。特别是看我对那个问题的回答,因为它描述了如何将随机文本文件加载为 Python 脚本:https://stackoverflow.com/a/43602557/2988730:

from importlib.util import spec_from_loader, module_from_spec
from importlib.machinery import SourceFileLoader 

spec = spec_from_loader("bot_config_data", SourceFileLoader("bot_config_data", "C:/Users/Dave/Desktop/Bot/bot_config_data.py"))
bot_config_data = module_from_spec(spec)
spec.loader.exec_module(bot_config_data)
price_data = bot_config_data.price_data

最后说明:您似乎盲目地将路径中的所有 \\ 转换为 //。正斜杠不需要转义,因此您只需要单个正斜杠。如果您想避免转义反斜杠,请将r 放在字符串前面,使其成为raw string

【讨论】:

  • “bot_config_data”参数应该是“bot_config_data.price_data”。
  • @Dave。在哪里? bot_config_data 是一个模块,price_data 是该模块的一个属性。即使您只需要一个属性,最终也将不得不加载整个模块。
  • SourceFileLoader() 的参数?否则,它会抛出 ImportError?
  • 已修复。规范中有一个额外的 price_data
【解决方案2】:

如果您需要使用动态名称而不是硬编码来导入 import 语句,可以使用 importlib.import_module:

price_data = importlib.import_module('bot_config_data').price_data

嗯?那是怎样的“动态”? 'bot_config_data' 实际上是一个字符串文字。

如果您真的想动态导入模块,与普遍认为的相反,唯一的方法是使用 exec()。我挑战任何人寻找或提交 importlib.import_module 或 __import__ 用于真正动态导入模块的示例。

要明确:__import__ 允许动态处理导入,这与动态导入不同,因为那是只执行的鸡/蛋类型问题() 可以解决。

问题是 importlib.import_module() 返回一个需要赋值的对象。 (它不只是像通常暗示的那样添加到全局命名空间。)但是您不能在赋值的两侧使用相同的参数。因此,虽然它允许您以更动态的方式处理导入,但它无法提供动态导入模块的方式。

【讨论】:

    猜你喜欢
    • 2018-04-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-11
    • 2014-06-03
    • 1970-01-01
    相关资源
    最近更新 更多