Создание динамического модуля
Я хотел бы динамически создавать модуль из словаря, и мне интересно, действительно ли добавление элемента в 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.