Как динамически загружать модули при импорте пакетов? - PullRequest
3 голосов
/ 04 января 2010

Учитывая следующий пример макета:

test/
  test.py
  formats/
    __init__.py
    format_a.py
    format_b.py

Я пытаюсь заархивировать то, что всякий раз, когда я import formats, __init__.py ищет все доступные модули в подкаталоге formats, загружает их и делает их доступными (прямо сейчас просто через переменную, supported_formats ). Если есть лучший, более питонический или иной подход к динамической загрузке содержимого во время выполнения, основанный на физических доступных файлах, пожалуйста, сообщите.

Мой подход

Я пробовал что-то вроде этого (в __init__.py):

supported_formats =  [__import__(f[:f.index('.py')]) for f in glob.glob('*.py')]

Пока я просто заставляю его работать, когда запускаю __init__.py из командной строки (из subdir форматов или из других каталогов). Но когда я импортирую его из test.py, он выдается мне вот так:

ImportError: No module named format_a.py

То же самое, когда я импортирую его из интерпретатора python, когда я запускаю интерпретатор в другом каталоге, кроме подкаталога formats.

Вот весь код. Он также ищет определенный класс и хранит один экземпляр каждого класса в dict, но основная часть, которую я не получаю, - динамическая загрузка модулей:

def dload(get_cls=True, get_mod=True, key=None, fstring_mod='*.py', fstring_class=''):
  if p.dirname(__file__):
    path = p.split(p.abspath(__file__))[0]
    fstring_mod = p.join(path, fstring_mod)
    print >> sys.stderr, 'Path-Glob:', fstring_mod
  modules = [p.split(fn)[1][:fn.index('.py')] for fn in glob.glob(fstring_mod)]
  print >> sys.stderr, 'Modules:', ', '.join(modules)
  modules = [__import__(m) for m in modules]
  if get_cls:
    classes = {} if key else []
    for m in modules:
      print >> sys.stderr, "-", m
      for c in [m.__dict__[c]() for c in m.__dict__ if c.startswith(fstring_class)]:
        print >> sys.stderr, " ", c
        if key:
          classes[getattr(c, key)] = c
        else:
          classes.append(c)
    if get_mod:
      return (modules, classes)
    else:
      return classes
  elif get_mod:
    return modules

_supported_formats = dload(get_mod=False, key='fid', fstring_mod='format_*.py', fstring_class='Format')

Моя идея

Весь беспорядок с путями файловой системы и тому подобным, вероятно, в любом случае запутан. Я хотел бы справиться с этим с помощью пространств имен модулей или чего-то подобного, но сейчас я немного заблуждаюсь о том, как запускать и как обращаться к модулям, чтобы они были доступны из любого места.

Ответы [ 5 ]

1 голос
/ 05 января 2010

Сначала вы должны сделать так, чтобы ваш код работал независимо от текущего рабочего каталога. Для этого вы используете переменную __file__. Вы также должны использовать абсолютный импорт.

Так что-то вроде (не проверено):

supported_formats = {}
for fn in os.listdir(os.path.dirname(__file__)):
    if fn.endswith('.py'):
        exec ("from formats import %s" % fn[:-3]) in supported_formats
1 голос
/ 04 января 2010

В код необходимо внести два исправления:

  1. Вы должны позвонить __import__(m, globals(), locals()) вместо __import__(m).Это необходимо для Python, чтобы найти модули в пакете.

  2. Ваш код неправильно удаляет расширение .py, так как вы вызываете index () для неправильной строки.Если это всегда расширение .py, вы можете просто использовать p.split(fn)[1][:-3].

0 голосов
/ 06 января 2010

Вот код, который я придумал после исправлений от interjay. Все еще не уверен, что это хороший стиль.

def load_modules(filemask='*.py', ignore_list=('__init__.py', )):
  modules = {}
  dirname = os.path.dirname(__file__)
  if dirname:
    filemask = os.path.join(dirname, filemask)
  for fn in glob.glob(filemask):
    fn = os.path.split(fn)[1]
    if fn in ignore_list:
      continue
    fn = os.path.splitext(fn)[0]
    modules[fn] = __import__(fn, globals(), locals())
  return modules
0 голосов
/ 04 января 2010

Я подумал, что если вы сделаете что-то такое, «format» будет вашим пакетом, поэтому, когда вы скажете это import formats, вы сможете получить доступ к остальным модулям внутри этого пакета, так что вы получите что-то вроде formats.format_a.your_method

Не уверен, хотя, я просто n00b.

0 голосов
/ 04 января 2010

Модуль ищется в sys.path. Вы должны иметь возможность расширять sys.path путем указания пути к вашему модулю. Я также не совсем уверен, можете ли вы загрузить модуль в sys.path с соглашением 'module.py', я думаю, что предпочтительнее использовать '.py'.

Это, очевидно, не решение, но, тем не менее, может пригодиться.

...