Единственным хитроумным приемом в этом сценарии является использование встроенной функции __import__, импортирующей модуль по его имени в виде строки. Взглянем на следующие две строки из функции addComponents сценария:
module = __import__(demo) # импортировать по имени в виде строки
part = module.Demo(root) # прикрепить экземпляр, созданный его классом Demo
Они эквивалентны следующим строкам:
import ‘demoDlg’
part = ‘demoDlg’.Demo(root)
Однако последние две инструкции являются недопустимыми в языке Python — имя модуля в инструкциях импорта должно быть идентификатором Python, а не строковым значением. Кроме того, имена модулей в инструкциях импорта интерпретируются буквально (то есть не вычисляются), и в точечной нотации идентификаторы должны выражать объект (а не строку с именем). Для обеспечения общности функция addComponents обходит список строк с именами и с помощью __import__ импортирует и возвращает модуль, идентифицируемый каждой строкой. Фактически действие цикла for эквивалентно следующим инструкциям:
import demoDlg, demoRadio, demoCheck, demoScale
part = demoDlg.Demo(root)
part = demoRadio.Demo(root)
part = demoCheck.Demo(root)
part = demoScale.Demo(root)
Использование в сценарии списка строк с именами упрощает изменение набора встраиваемых демонстраций — достаточно лишь изменить список, не трогая выполняемый код. Кроме того, такая реализация, управляемая данными, оказывается более компактной, менее избыточной и проще в отладке и в сопровождении. Между прочим, имеется еще одна возможность импортировать модули по их именам в виде строк, динамически создавая и выполняя инструкции импорта, как показано ниже:
for demo in demoModules:
exec(‘from %s import Demo’ % demo) # сконструировать и выполнить from part = eval(‘Demo’)(root) # получить ссылку на импортированный
# объект по его имени в виде строки
Функция exec компилирует и выполняет строку с инструкцией Python (в данном случае from, загружающей класс Demo из модуля). Она действует, как если бы вместо вызова функции exec в исходный текст была вставлена строка с инструкцией. Следующий фрагмент позволяет добиться того же эффекта, но конструируя инструкцию import:
for demo in demoModules: exec(‘import %s’ % demo) # сконструировать и выполнить import part = eval(demo).Demo(root) # получить ссылку на объект модуля # также по имени в виде строки
Так как функции exec/eval поддерживают любые инструкции Python, этот прием оказывается более универсальным, чем использование функции __import__, но он может замедлять работу, потому что при этом требуется производить синтаксический анализ строк программного кода перед их выполнением.[XXXVI] Такое замедление может быть несущественным для графических интерфейсов — пользователи работают значительно медленнее, чем синтаксические анализаторы.
Использованная литература:
Марк Лутц — Программирование на Python, 4-е издание, I том, 2011