Создание динамического модуля

Я хотел бы динамически создавать модуль из словаря, и мне интересно, действительно ли добавление элемента в sys.modules - лучший способ сделать это. EG

context = { a: 1, b: 2 }
import types
test_context_module = types.ModuleType('TestContext', 'Module created to provide a context for tests')
test_context_module.__dict__.update(context)
import sys
sys.modules['TestContext'] = test_context_module

Моя ближайшая цель в этом отношении заключается в том, чтобы предоставить контекст для выполнения теста времени:

import timeit
timeit.Timer('a + b', 'from TestContext import *')

Кажется, что есть другие способы сделать это, поскольку конструктор Timer принимает объекты, а также строки. Мне все еще интересно узнать, как это сделать, поскольку а) у него есть другие потенциальные приложения; и б) я не уверен точно, как использовать объекты с конструктором Timer; в некоторых случаях это может оказаться менее подходящим, чем этот подход.

редактирует /Revelations/PHOOEYS/EUREKAE:

  • Я понял, что пример кода, связанный с запуском тестов времени, на самом деле не работает, потому что import * работает только на уровне модуля, а контекст, в котором выполняется этот оператор, - это функция в модуле testit. Другими словами, словарь globals, используемый при выполнении этого кода, относится к __main__, так как тот, где я был, когда я написал код в интерактивной оболочке. Так что логика для выяснения этого является немного неудачной, но это все еще правильный вопрос.

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

  • Подобный-но-тонкий случай динамической загрузки модуля из файла, который не находится в python, включает путь, который легко выполняется с помощью imp.load_source('NewModuleName', 'path/to/module/module_to_load.py'). Это загружает модуль в sys.modules. Однако это не отвечает на мой вопрос, потому что, действительно, что, если вы используете python на встроенной платформе без файловой системы?

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

Но вопрос, по сути, на этом этапе заключается в том, как установить глобальный (то есть модуль) контекст для объекта. Может быть, я должен спросить об этом более конкретно? И в большей степени, как заставить Python делать это, пока объекты shoehorning в данный модуль?

Ответ 1

Хм, ну, я могу вам сказать, что функция timeit фактически выполняет свой код с использованием глобальных переменных модуля. Итак, в вашем примере вы можете написать

import timeit
timeit.a = 1
timeit.b = 2
timeit.Timer('a + b').timeit()

и это сработает. Но это не относится к вашей более общей проблеме определения модуля динамически.

Что касается проблемы определения модуля, это определенно возможно, и я думаю, что вы натолкнулись на лучший способ сделать это. Для справки, суть того, что происходит, когда Python импортирует модуль, в основном такова:

module = imp.new_module(name)
execfile(file, module.__dict__)

То же самое, что вы делаете, за исключением того, что вы загружаете содержимое модуля из существующего словаря, а не из файла. (Я не знаю никакой разницы между types.ModuleType и imp.new_module, отличной от docstring, поэтому вы можете использовать их взаимозаменяемо). Что вы делаете, это несколько похоже на написание собственного импортера, и когда вы это делаете, вы можете наверняка столкнуться с sys.modules.

В стороне, даже если ваша import * вещь была законной внутри функции, у вас могут быть проблемы, потому что, как ни странно, утверждение, которое вы передаете Timer, похоже, не распознает его собственные локальные переменные. Я вызвал немного Python voodoo по имени extract_context() (это функция, которую я написал), чтобы установить a и b в локальной области и запустил

print timeit.Timer('print locals(); a + b', 'sys.modules["__main__"].extract_context()').timeit()

Конечно, распечатка locals() включала a и b:

{'a': 1, 'b': 2, '_timer': <built-in function time>, '_it': repeat(None, 999999), '_t0': 1277378305.3572791, '_i': None}

но он все еще жаловался NameError: global name 'a' is not defined. Weird.