使用exec加载模块变量,使用文件路径

时间:2018-02-27 15:52:01

标签: python

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

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

UPDATE2 使用Mad Physicist的答案,我的代码将是:

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

2 个答案:

答案 0 :(得分:2)

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

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_data最终在sys.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

答案 1 :(得分:0)

如果您需要使用动态名称而不是硬编码进行导入 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()返回一个需要分配的对象。 (它不仅像通常暗示的那样添加到全局名称空间。)但是,您不能在赋值的两侧使用相同的参数。因此,尽管它允许您以更动态的方式处理导入,但它没有提供动态导入模块的方式。