22

我想动态加载一个模块,给定它的字符串名称(来自环境变量)。我正在使用 Python 2.7。我知道我可以做类似的事情:

import os, importlib
my_module = importlib.import_module(os.environ.get('SETTINGS_MODULE'))

这大致相当于

import my_settings

(哪里SETTINGS_MODULE = 'my_settings')。问题是,我需要一些相当于

from my_settings import *

因为我希望能够访问模块中的所有方法和变量。我试过了

import os, importlib
my_module = importlib.import_module(os.environ.get('SETTINGS_MODULE'))
from my_module import *

但我得到一堆错误这样做。有没有办法在 Python 2.7 中动态导入模块的所有方法和属性?

4

3 回答 3

20

如果你有你的模块对象,你可以模仿逻辑import *使用如下:

module_dict = my_module.__dict__
try:
    to_import = my_module.__all__
except AttributeError:
    to_import = [name for name in module_dict if not name.startswith('_')]
globals().update({name: module_dict[name] for name in to_import})

然而,这几乎可以肯定是一个非常糟糕的主意。您将毫不客气地踩踏任何具有相同名称的现有变量。当您正常执行此操作时,这已经够糟糕了from blah import *,但是当您动态执行此操作时,对于哪些名称可能会发生冲突则更加不确定。您最好只导入my_module然后使用常规属性访问(例如,my_module.someAttr)从中访问您需要的内容,或者getattr如果您需要动态访问其属性。

于 2014-01-19T19:22:26.963 回答
2

没有按照措辞准确回答问题,但如果您希望将文件作为动态模块的代理,您可以使用__getattr__在模块级别定义的功能。

import importlib
import os

module_name = os.environ.get('CONFIG_MODULE', 'configs.config_local')
mod = importlib.import_module(module_name)

def __getattr__(name):
    return getattr(mod, name)
于 2019-10-14T15:26:15.570 回答
0

我的情况有点不同 - 想要动态导入每个gameX.__init__.py模块中的 constants.py 名称(见下文),因为静态导入这些名称会使它们永远留在 sys.modules 中(请参阅:我从这个相关问题中挑选的 Beazley摘录) .

这是我的文件夹结构:

game/
 __init__.py
 game1/
   __init__.py
   constants.py
   ...
 game2/
   __init__.py
   constants.py
   ...

每个都gameX.__init__.py导出一个 init() 方法 - 所以我最初from .constants import *在所有gameX.__init__.py我试图在 init() 方法中移动的方法中都有一个。

我的第一次尝试是:

@@ -275,2 +274,6 @@ def init():
     # called instead of 'reload'
+    yak = {}
+    yak.update(locals())
+    from .constants import * # fails here
+    yak = {x: y for x,y in locals() if x not in yak}
+    globals().update(yak)
     brec.ModReader.recHeader = RecordHeader

以相当神秘的方式失败:

SyntaxError: import * is not allowed in function 'init' 因为它包含一个带有自由变量的嵌套函数

我可以向你保证那里没有嵌套函数。无论如何,我砍伐并削减并最终得到:

def init():
    # ...
    from .. import dynamic_import_hack
    dynamic_import_hack(__name__)

在哪里game.__init__.py

def dynamic_import_hack(package_name):
    print __name__ # game.init
    print package_name # game.gameX.init
    import importlib
    constants = importlib.import_module('.constants', package=package_name)
    import sys
    for k in dir(constants):
        if k.startswith('_'): continue
        setattr(sys.modules[package_name], k, getattr(constants, k))

(对于 setattr,请参阅如何在运行时向模块添加属性?而对于 getattr,如何动态导入 python 模块函数? -我更喜欢使用这些而不是直接访问__dict__

这很有效,并且比接受的答案中的方法更通用,因为它允许您将黑客放在一个地方并从任何模块使用它。但是,我不确定这是实现它的最佳方式 - 本来要问一个问题,但由于它与这个问题重复,我将其发布为答案并希望得到一些反馈。我的问题是:

  • 为什么这个“语法错误:函数'init'中不允许导入*”而没有嵌套函数?
  • dir 在其文档中有很多警告- 特别是它试图产生最相关的,而不是完整的信息- 这个完整让我有点担心
  • 没有内置的方法import *吗?即使在 python 3 中?
于 2017-01-25T09:45:44.493 回答