Лутц М. - Изучаем Python (1077325), страница 118
Текст из файла (страница 118)
Полное имя направляет интерпретатор к имени в указанном объекте модуля, а не к имени в импортирующем модуле (пеасесгЗру)1 сарогс певсвб1 а Иипортировать модуль целиком пвзсеб1.х = ВВ а Ок: изменяется имя х в пезгеб1 пввсеб1.рыптег() % руспоп пезсебз.ру 88 Глаза 21. Дополнительные возможности модулей № Плохо: пожег незаиетно перезаписать мои имена № Еае хуже: нет никакого способа понять, № нто ми получили! »> Ггоа аоео1ет 1врогт * »> тгоа аоео1е2 1врогт ° »> Ггоа аоео1ез 1арогт ° >» »> топо() № ну?зз Решение опять же заключается в том, чтобы так не делать: старайтесь явно перечислять требуемые атрибуты в инструкции Г гов и ограничивайте применение формы 1гов * одним модулем на файл. В этом случае любые неопределенные имена, согласно дедуктивному методу, должны находиться в модуле, который импортируется единственной инструкцией Ггов *. Этой проблемы вообще можно избежать, если всегда использовать инструкцию 1врогт вместо г гов, но это слишком сложно— как и многое другое в программировании, инструкция Ггов — очень удобный инструмент при разумном использовании.
Функция ге!оад может не оказывать влияния, если импорт осуществлялся инструкцией 1гопт Это еще одна ловушка, связанная с инструкцией ггов: как уже говорилось ранее„инструкция Г гов копирует (присваивает) имена при выполнении, поэтому нет никакой обратной связи с модулем, откуда были скопированы имена. Имена, скопированные инструкцией Ггов, просто становятся ссылками на объекты, на которые ссылались по тем же именам в импортируемом модуле, когда была выполнена инструкция Г гав. Вследствие этого повторная загрузка импортируемого модуля может не оказывать воздействия на клиентов, которые импортировали его имена с помощью инструкции Ггов. 1'о есть имена в модулях-клиентах будут по-прежнему ссылаться на оригинальные объекты, полученные инструкцией Ггов, даже если имена в оригинальном модуле будут переопределены: ггоа вобо1е )арогт х № х может не измениться е результате перезагрузки' Инструкция гогот а может затушевывать смысл переменных Я упоминал об этом в главе 19, но оставил подробности до этого момента.
Поскольку в инструкции Ггов вобо1е 1врогт * не указываются необходимые имена переменных, она может непреднамеренно перезаписать имена, уже используемые в области видимости импортирующего модуля. Хуже того, это может осложнить поиск модуля, откуда исходит переменная, вызвавшая проблемы. Это особенно верно, когда форма инструкции Г гов * используется более чем в одном импортированном файле. Например, если инструкция Ггов * применяется к трем модулям, то у вас не будет иного способа узнать, какая в действительности вызывается функция, кроме как выполнить поиск в трех разных файлах модулей (каждый из которых может находиться в отдельном каталоге): Типичные проблемы при работе с модулями ге1оас(вооо1е) Х № Изиенится модуль, но на иои имена № По-преинеиу ссылается на старый обьакт Чтобы сделать повторную загрузку более эффективной„ используйте инструкцию 1врогт и полные имена переменных вместо инструкции ( гоп.
Поскольку полные имена всегда ведут обратно в импортированный модуль, после повторной загрузки они автоматически будут связаны с новыми именами в модуле. № Получит~ обьект модуля, а не пиона ~враг( восс1е ге1оао(восо1е) № Изменит непосредственно саи объект иодуля восо1е.Х № Текущее значение Х отращзет результат лереззгрузки В главе 3 уже говорилось, что из-за возникающих сложностей лучше не использовать операции импорта и повторной загрузки для запуска программ.
Дело еще больше осложняется, когда в игру вступает инструкция Тгов. Начинающие осваивать язык Ру1Ьоп часто сталкиваются с проблемой, описываемой здесь. Представим, что после открытия модуля в окне редактирования текста вы запускаете интерактивный сеанс, чтобы загрузить и протестировать модуль с помощью инструкции т гоп: Ггсщ ВООо1е тврогт Топо(топ топсттоп(т, 2. 3) Отыскав ошибку, вы возвращаетесь в окно редактирования, исправляете ее и пытаетесь повторно загрузить модуль следующим способом: ге1оаб(васо1е) Но вы не получите ожидаемого эффекта — инструкция ( гов создала имя Топо(1оп, но не создала имя вобо1е.
Чтобы сослаться на модуль в функции ге1оаб, его сначала необходимо импортировать инструкцией 3врогт: тврогт восо1е ге1оаб(восо1е) топо(топ(т. 2, 3) Однако и в этом случае вы не получите ожидаемого эффекта — функция ге!оаб обновит объект модуля, но, как говорилось в предыдущем разделе, имена, такие как Топо(топ, скопированные нз модуля ранее, по-прежнему продолжают ссылаться на старые объекты (в данном случае — на первоначальную версию функции). Чтобы действительно получить доступ к новой версии функции„после перезагрузки модуля ее необходимо вызывать как вобо1е.
Топсттоп или повторно запустить инструкцию Т гов: 1врогт вооо1е ге1оас(восо1е) тгов вооо1е 1врогт гопстшп Топот(оп( 1, 2, 3) ге!оаэи, 1гот и тестирование в интерактивной оболочке Глава 21. Дополнительные возможности модулей Функция ге! оаэи не обладает транзитивностью Когда выполняется повторная загрузка модуля, интерпретатор перезагружает только данный конкретный файл модуля — он не выполняет повторную загрузку модулей, которые были импортированы перезагружаемым модулем.
Например, если выполняется перезагрузка некоторого модуля А, и А импортирует модули В и С, перезагружен будет только модуль А, но не В и С. Инструкции внутри модуля А, которые импортируют модули В и С, будут перезапущены в процессе перезагрузки, но они просто вернут объекты уже загруженных модулей В и С (предполагается, что к этому моменту они уже были импортированы). Чтобы было более понятно, ниже приводится пример содержимого файлаАру: Ф Эти иодупи не будут перезагруиены виесте с д Ф Просто будут иипортированы уие загруиенные иодупи тарагт В гврогт С в рутлоп »> »> га1оас(А) Не следует полагаться на транзитивную перезагрузку модулей — лучше несколько раз вызывайте функцию ге1оаб для непосредственного обновления субкомпонентов.
При желании можно предусмотреть в программе автоматическую перезагрузку ее компонентов, добавив вызовы ге1оаб в родительские модули, каким здесь является А. Кще лучше было бы написать универсальный инструмент для автоматического выполнения рекурсивной перезагрузки модулей, сканируя содержимое атрибутов б(ст модулей (смотрите раздел «Модули— это объекты: метапрограммы«, выше в этой главе) и проверяя атрибут туре в каждом элементе (глава 9), чтобы отыскать вложенные модули. Такие вспомогательные функции могут рекурсивно вызывать сами себя и обходить произвольные цепочки импортирования.
Теперь наконец-то нам удалось запустить новую версию функции. Как видите, прием, основанный на использовании функции ге1оаб в паре с инструкцией Ггов, имеет врожденные проблемы: вы не только должны не забывать перезагружать модуль после импорта, но и не забывать повторно запускать инструкции 1 гсв после перезагрузки модуля. Это достаточно сложно, чтобы время от времени сбивать с толку даже опытного программиста.
Вы не должны ожидать, что функция гс1оаб и инструкция Ггоэ будут безукоризненно работать в паре. Лучше всего вообще не объединять их— используйте функцию ге1оаб в паре с инструкцией тврогт или запускайте свои программы другими способами, как предлагалось в главе 3 (например, выбирая пункт Ввп-+Ввп Модо(е (Запустить — +Запустить модуль) в меню среды разработки 1Р1Л щелчком мыши на ярлыке файла или из командной строки системы).
ипичные проблемы при работе с модулями Чтобы воспользоваться этой утилитой, импортируйте функцию ге1оаб а11 и передайте ей имя уже загруженного модуля (как если бы это была встроенная функция ге1оаб). Когда файл запускается как самостоятельная программа, его код самопроверки выполнит проверку самого модуля — он должен импортировать самого себя, потому что его собственное имя не будет определено в файле без инструкции импортирования. Я рекомендую поэкспериментировать с этим примером самостоятельно: сврогс суреа оег зсасоз(вооо1е) ргюс 'ге1оасспр', вось1е.
паве оес сгапзсссче ге!оао(вооо1е. чсз1сео): 11 по( чсмтес.оаа Хеу(вобо1е) зсасчз(вооч1е) ге1оао(восч1е) чсзстес[восч1е] .†. Иопе сог ассгоЬ) сп восо1е. Отсо .ча1чез(): В Для всех атрибутов сг суре(ассгоь)) == суреа иооо1етуре; в Рекурсия, если иодуль сгапюссче ге1оао(ассгоь), ч1зссео) В Пропустить повторные посевения В Перезагрузить иодуль в И посетит~ дочерние модули оес ге1оас а11(*агре) чсзссео = ( Гог ага сп агре сг суре(агр) == (урез иоон1етуре: сгапзсссче ге1оао(агр, чсысео) сс паве == ' васи сврогт ге1оаоа11 ге1оас а11(ге1оаса11) В Тест перезагрузить свиого себя Ф Должна перезагрузить этот иодуль в и модуль Сурез Рекурсивный импорт с инструкцией 1гот может не работать Напоследок я оставил самую странную (и, к счастью, малоизвестную) проблему.