Лутц М. - Изучаем Python (1077325), страница 88
Текст из файла (страница 88)
А пока, особенно если вы не имеете достаточного опыта программирования, по мере возможности избегайте искушения использовать глобальные переменные (старайтесь организовать обмен данными через параметры и возвращаемые значения).
Шесть месяцев спустя вы и ваши коллеги будете рады, что поступали таким образом. Минимизируйте количество изменений в соседних файлах В этом разделе описывается еще одна проблема, связанная с областями видимости: несмотря на то, что существует возможность непосредственно изменять переменные в другом файле, этого следует избегать. Рассмотрим следующие два модуля: р гггет ру Х = 99 Х ЕЕСОее Ру трогт ттгет Гггет Х = 88 Первый модуль определяет переменную Х, а второй — изменяет ее значение в инструкции присваивания. Обратите внимание, что для этого во втором модуле необходимо импортировать первый модуль. Как вы уже знаете, каждый модуль представляет собой отдельное пространство имен (где размещаются переменные), поэтому, чтобы увидеть содержимое одного модуля во втором, его необходимо импортировать. В терминах этой главы глобальная область видимости модуля после импор- 409 Инструкция о!оЬа) тирования лревращаелтся в пространство имен атрибутов объекта модуля — импортирующий модуль автоматически получает доступ ко всем глобальным переменным импортируемого модуля, поэтому при импортировании глобальная область видимости модуля, по сути, трансформируется в пространство имен атрибутов.
После импортирования первого модуля второй модуль присваивает его переменной новое значение. Проблема состоит в том, что эта операция выполняется слишком неявно: для любого, кто занимается сопровождением или использует первый модуль, будет сложно догадаться, что какой-то другой модуль, далеко отстоящий в цепочке импорта, может изменить значение переменной Х. В конце концов, второй модуль может находиться вообще в другом каталоге, из-за чего его сложно будет найти. Здесь также устанавливается слишком тесная зависимость между этими двумя модулями, потому что оба они зависят от значения переменной Х, будет трудно понять или повторно использовать один модуль без другого.
И этом случае лучшая рекомендация — не использовать такую возможность; лучше организовать взаимодействие между модулями через вызовы функций, передавая им аргументы и получая возвращаемые значения. В данном конкретном случае было бы лучше добавить функцию доступа, которая будет выполнять изменения: я гтгы ру Х = 99 Оет еетХ(пен): 9)оеа) Х Х = пен я ееоопс, ру терогт Г!гет Гтгш .еетХ(88) Для этого потребуется добавить дополнительный программный код, но он имеет огромное значение в смысле обеспечения удобочитаемости и удобства в сопровождении — когда тот, кто впервые будет знакомиться с модулем, увидит функцию, он будет знать, что это часть интерфейса модуля и поймет, что переменная Х может изменяться.
Мы не можем полностью избавиться от изменений в соседних файлах, однако здравый смысл диктует необходимость минимизировать их число, если это не является шир ко распространенным явлением в программе. Другие способы доступа к глобальным переменным Интересно, что благодаря трансформации глобальных переменных в атрибуты объекта загруженного модуля существует возможность имитировать инструкцию 91оза1, импортируя вмещающий модуль и выполняя присваивание его атрибутам, как показано в следующем примере модуля.
Программный код в этом файле в одном случае импортирует вмещающий модуль по имени, а в другом использует таблицу загру- женных модулей яуя. аобо1ея (подробнее об этой таблице рассказывает- ся в главе 21): № тьтяаоб.ру чаг = 99 № Глобальная переменнзя == атрибут модуля бег 1оса1(); чаг = 0 № Изиеняется локальнзя переменная бег 91ош(): 91оЬа1 чаг чаг += 1 № Глобальное объявление (обичное) № Изменяется глобальнзя переменная № Изиеняется локзльмая переменная № Иипорт самого себя № Изиеняется глобальная переменная бег 91ЬЬЗ(). чаг = 0 тарогт вув 91оЬ = яув.аобо1ев('тптяаоб'] 91оЬ. чаг += 1 бет тевт(): ргтпт чаг 1оса1(); 91ао1(). 91оЬ2(); 91оЬЗ() ргтпт чаг После запуска будут добавлены 3 глобальные переменные (только пер- вая функция ничего не добавляет): Этот пример иллюстрирует эквивалентность глобальных имен и атри- бутов модуля, однако, чтобы явно выразить свои намерения, нам по- требовалось написать немного больше, чем при использовании инст- рукции 910Ьа1 ° Области видимости и вложенные функции До сих пор мы не еще рассматривали одну часть правила области видимости в языке Ру()топ (просто потому, что с нею редко сталкиваются на практике).
Однако пришло время более пристально посмотреть на Е в правиле ] ЕСВ. Уровень Е появился относительно недавно (он был добавлен в РУФ]топ 2.2) — это локальные области видимости объемлющих инструкций бет. Иногда объемлющие области видимости называ- бег 91ЬЬ2(); чаг = 0 тарогт тптяаоб тптваоб.чаг ъ= 1 »> 1арогт тлтваоб »> тлтваоб,тевт() 99 102 »> татваоб,чаг 102 Глава 16. Области видимости и аргументы № Изменяется локальная переменная № Импорт системной тзблици № Получить объект модуля № (или использоазть паае ) № Изиеняется глобальнзя переменная 411 Области видимости и вложенные функции ют статически вложенными областями видимости. В действительности вложение является лексическим — вложенные области видимости соответствуют физически вложенным блокам программного кода в исходных текстах программы.
о В В Ру1Ьол 3.0 предполагается появление инструкции псп1оса1, которая позволит получать доступ на запись к переменным в областях видимости объемлющих функций, так же, как нынешняя инструкция 81опа1 позволяет получить доступ на запись к области видимости объемлющего модуля. Синтаксически эта инструкция будет выглядеть так же, как инструкция 81сса1, только в ней будет использоваться слово прп1ссэ1. Однако это в будущем, поэтому дополнительную информацию ищите в примечаниях к выпуску 3.0.
Вложенные области видимости С появлением областей видимости вложенных функций правила поиска переменных стали немного более сложными. Внутри функции: ° Операция присваивания (Х = ыз1ие) создает или изменяет имя Х в текущей локальной области видимости по умолчанию. Если имя Х объявлено глобальным внутри функции, операция присваивания создает или изменяет имя Х в области видимости объемлющего модуля. ° При обращении к переменной (Х) поиск имени Х сначала производится в локальной области видимости (функции); затем в локальных областях видимости всех лексически объемлющих функций, изнутри наружу; затем в текущей глобальной области видимости (в модуле); и, наконец, во встроенной области видимости (модуль Ьс)111п ). Поиск имен, объявленных в инструкции 01сЬ81, начинается сразу с глобальной области видимости. Обратите внимание, что инструкция с1ЬЬа1 отображает имена в область видимости объемлющего модуля.
Когда имеются вложенные функции, можно получить значения переменных в объемлющих функциях, но их нельзя изменить. Чтобы пояснить эти положения, рассмотрим их на примере программного кода. Примеры вложенных областей видимости Ниже приводится пример вложенной области видимости: сег 111): х = 88 Сеу 12<); рттпс х 12() 11( ) Ф Выведет ВВ Прежде всего — это вполне допустимый программный код на языке РуФоп: инструкция Ьет — это обычная исполняемая инструкция, которая 412 Глава 16. Области видимости и аргументы может появляться в любом месте программы, где могут появляться любые другие инструкции, включая вложение в другую инструкцию Ое(. В этом примере вложенная инструкция бе( исполняется в момент вызова функции (1 — она создает функцию и связывает ее с именем т2, которое является локальным и размещается в локальной области видимости функции (1.
В некотором смысле (2 — это временная функция, которая существует только во время работы (и видима только для программного кода) объемлющей функции т1. Однако, обратите внимание, что происходит внутри функции (2: когда производится вывод переменной х, она ссылается на переменную х в локальной области видимости объемлющей функции (1. Функции имеют возможность обращаться к именам, которые физически располагаются в любых объемлющих инструкциях бег, и имя х в функции (2 автоматически отображается на имя х в функции Г! в соответствии с правилом поиска з ЕОВ. Это правило поиска в объемлющих областях видимости выполняется, даже если объемлющая функция фактически уже вернула управление.