2014-01-19 3 views
12

Я бы хотел загрузить модуль динамически, учитывая его имя строки (из переменной окружения). Я использую Python 2.7. Я знаю, что могу сделать что-то вроде:Python: как динамически импортировать все методы и атрибуты из модуля

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?

+0

Можете ли вы не просто сделать это с 'eval'? –

ответ

12

Если у вас есть объект модуля, вы можете имитировать логика 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, если вам нужно динамически получить доступ к его атрибутам.

+0

Я решил просто установить Django's DJANGO_SETTINGS_MODULE переменную окружения вместо того, чтобы загружать переменные в default.py. Похоже, это правильный ответ, поэтому, спасибо за помощь! – sundance

+0

Я взял на себя смелость редактировать код, который лучше соответствует поведению 'import *'. Не стесняйтесь возвращаться, если у меня что-то не так или что-то изменило намерение. – user2357112

0

Мое дело было несколько иным - хотелось динамически импортировать имена констант.py в каждом модуле gameX.__init__.py (см. Ниже), вызывая их статический импорт, навсегда оставит их в sys.modules (см. Этот excerpt от Beazley, которого я выбрал от this related question).

Вот моя структура папок:

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

Каждый gameX.__init__.py экспортирует метод Init() - так что я изначально имел from .constants import * во всех тех gameX.__init__.py, которые я пытался двигаться внутри инициализации() метод.

Моя первая попытка в строках:

@@ -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' because it contains a nested function with free variables

Я могу заверить вас, нет вложенные функции там.Во всяком случае я взломал и полоснул и в конечном итоге с:

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 см How can I add attributes to a module at run time? время для GetAttr How can I import a python module function dynamically? - Я предпочитаю использовать те, чем прямой доступ к __dict__)

Это работает, и это более общий, чем подход в принятом ответе, потому что он позволяет вам взломать в одном месте и использовать его из любого модуля. Тем не менее, я не уверен, что это лучший способ его реализовать - собирался задать вопрос, но, поскольку это будет дубликат этого, я отправляю его как ответ и надеюсь получить некоторую обратную связь. Мои вопросы будут следующими:

  • Почему этот «SyntaxError: import * не разрешен в функции« init », пока нет вложенных функций?
  • режа имеет много предупреждений в its doc - в частности он пытается произвести наиболее релевантную, а не полную, информацию - это полного меня беспокоит немного
  • нет ли встроенного способа сделать import * ? даже в python 3?
+0

Из какой части вашей файловой структуры выполняется этот код верхнего уровня? Как было предложено в комментарии к первому вопросу, вы могли бы сделать это с помощью 'eval'? – sundance

+0

@sundance: Я узнал, что такое eval, тогда я забыл: P - файлы инициализации gameX импортируются динамически, а затем мы сохраняем тот, который нам нужен - нам нужно сделать это, чтобы гарантировать, что эти файлы есть и работают, но теперь вы упомянули об этом мы могли бы также избежать этого: см. https://github.com/wrye-bash/wrye-bash/blob/7cfecf2d13ddc763959b6129ccf9dd1cc69d166e/Mopy/bash/bush.py#L53-L68 (этот код также импортирует все остальное - код, который я разместил здесь, все еще находится на моем локальном дереве) –

+0

Задано здесь: http://stackoverflow.com/q/41897470/281545 –

Смежные вопросы