Python:如何动态地从模块导入所有方法和属性

时间:2014-01-19 19:15:27

标签: python python-2.7 python-import python-importlib

我想动态加载一个模块,给定它的字符串名称(来自环境变量)。我正在使用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中动态导入模块的所有方法和属性?

3 个答案:

答案 0 :(得分:16)

如果您有模块对象,则可以模仿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访问您需要的内容,如果您需要动态访问其属性。

答案 1 :(得分:0)

我的情况有点不同 - 想在每个gameX.__init__.py模块中动态导入constants.py名称(见下文),导致静态导入它们将永远留在sys.modules中(参见:this {{来自Beazley的我来自excerpt)。

这是我的文件夹结构:

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

相当神秘的失败:

  

语法错误:函数'init'中不允许import *,因为它包含带有自由变量的嵌套函数

我可以向你保证,那里没有嵌套函数。无论如何,我砍掉并砍掉了最后:

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,请参阅this related question,而对于getattr How can I add attributes to a module at run time? - 我更喜欢使用那些而不是直接访问__dict__

这是有效的,它比接受答案中的方法更通用,因为它允许你在一个地方进行黑客攻击并从任何模块中使用它。但是我并不确定这是实现它的最佳方式 - 我会问一个问题,但因为这将是一个问题的副本,我将其作为答案发布,并希望得到一些反馈。我的问题是:

  • 为什么这个“SyntaxError:import *在函数'init'中是不允许的”而没有嵌套函数?
  • dir在How can I import a python module function dynamically?中有很多警告 - 特别是它会尝试生成最相关但非完整的信息 - 这个完整让我担心有点
  • 没有内置方式来import *吗?甚至在python 3?

答案 2 :(得分:0)

不能精确地回答措辞上的问题,但是如果您希望将文件作为动态模块的代理,则可以使用在模块级别定义__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)