Лутц М. - Изучаем Python (1077325), страница 101
Текст из файла (страница 101)
Глобальные переменные (т. е. имена в объемлющем модуле) обычно далеко не самый лучший способ организации взаимодействий с функциями. Онн могут порождать зависимости и проблемы согласованности, которые существенно осложняют отладку программ. ° Взаимодействие; не воздействуйте на изменяемые аргументы, если вызывающая программа не предполагает этого. Функции могут оказывать воздействие на части изменяемых объектов, получаемых в виде аргументов, но, как и в случае с глобальными переменными, это предполагает слишком тесную связь между вызывающей программой и вызываемой функцией, что может сделать функцию слишком специфичной и неустойчивой. ° Связность: каждая функция должна иметь единственное назначение. Хорошо спроектированная функция должна решать одну задачу, которую можно выразить в одном повествовательном предложении. Если это предложение допускает слишком широкое толкование (например: «эта функция реализует всю программу целиком«) или содержит союзы (например: «эта функция дает возможность клиентам составлять и отправлять заказ на доставку пиццы«), то стоит подумать над тем, чтобы разбить ее на отдельные и более проетые функции.
В противном случае окажется невозможным повторно использовать программный код функции, в котором смешаны различные действия. ° Размер: каждая функция должна иметь относительно небольшой размер. Это условие естественным образом следует из предыдущего, однако если функция начинает занимать несколько экранов, — это 471 Концепции проектирования функций меняя информацию о его состоянии (например, зе!(.паве = 'зоб'). Кроме того, когда классы не используются, глобальные переменные часто представляют для функций в модуле наилучший способ сохранения состояния между вызовами.
Побочные эффекты в этом случае не опасны, потому что они ожидаемы. Функции — зто объекты: косвенный вызов Так как функции в языке Ру()топ во время выполнения являются объектами, можно написать такую программу, которая будет обрабатывать их в общем виде. Объекты функций могут присваиваться, передаться другим функциям, сохраняться в структурах данных и т. д., как если бы они были простыми числами или строками.
Мы встречали уже такие способы использования в более ранних примерах. Кроме того, объекты функций поддерживают специальные операции: они могут вызываться перечислением аргументов в круглых скобках, следующих сразу же за выражением функции. И тем не менее, функции принадлежат к той же категории, что и другие объекты. Например, в имени, которое используется в инструкции бе(, нет ничего уникального: это всего лишь переменная, которая создается в текущей области видимости, как если бы она стояло слева от знака =. После того как инструкция бе( будет выполнена, имя функции представляет собой всего лишь ссылку на объект — ее можно присвоить другим именам и вызывать функцию по любому из них (не только по первоначальному имени): »> Озт воэо(взззазв); № Ииени осло пРисваивается обьзкт функции рг1пт взззарз »> к = вояо № Теперь на зту функцию соцлается еве и иия и »> к('нв11о иог1ьн ') № Вызов объекта добавлением () Нз11о ногпн Поскольку аргументы передаются путем присваивания объектов, функции легко можно передавать другим функциям в виде аргументов.
В результате вызываемая функция может вызвать переданную ей функцию простым добавлением списка аргументов в круглых скобках: »> бзт 1пс1гзст(топо, ага): гцпо(ага) № Вызов обьвкта добавлением () »> зпп1гвот(вспо, 'нв11о )в11о)') № передача функции в функцию Не11о )в11о~ Существует даже возможность наполнять структуры данных функциями, как если бы они были простыми числами или строками. В этом нет ничего особенного, так как составные типы объектов могут содержать объекты любых типов: »> зоцвсц1в = ( (всцо, 'Зрав) '), (вояо, 'Нзв) ') ) »> Гог (Гцпс, аго) 1п зоявац1в: 472 Глава )7. Расширенные возможности функций гопс(агс) арап( Нае' В этом фрагменте просто выполняется обход списка зслебо1е и производится вызов функции еспо с одним аргументом (обратите внимание на операцию присваивания кортежа в заголовке инструкции цикла Тот, которая была представлена в главе 13).
Отсутствие описаний типов делает язык программирования Руг)топ невероятно гибким. Типичные ошибки при работе с функциями При работе с функциями вас поджидают подводные камни, о которых вы можете не догадываться. Они не всегда видны, некоторые из них исчезли в последних версиях, но большая часть оставшихся продол- жает ставить в тупик начинающих программистов. Локальные имена определяются статически »> Х = 99 »> Оег ве1есгог(): № Переиенная Х используется, но ей ничего № нв присваивается № Переиенная Х будет найдена в глобальной № области вид-ти ргтпг Х »> ве1есгог() 99 В этом фрагменте переменная Х внутри функции определяется как пе- ременная Х модуля. Но посмотрите, что произойдет, если добавить ин- струкцию присваивания переменной Х после ее использования: » > О ее ве1ес го г( ): рг1пг х № переиенная еие не суиествует! Х = 88 № Х классифицируется как локан~пал переиенная № То ие санов проискодит при "Чирогг Х, "Оет Х '.
Как известно, имена, которым выполняется присваивание внутри функции, по умолчанию рассматриваются как локальные — они располагаются в области видимости функции и существуют только во время работы функции. Но я еще не говорил, что локальные переменные определяются статически, во время компиляции программного кода в инструкции бег, а не в соответствии с операциями присваивания, производимыми во время выполнения.
Эта особенность становится причиной появления самых причудливых сообщений в группе новостей, получаемых от начинающих программистов. Обычно, если внутри функции имени не присваивается какое-либо значение, поиск его будет производиться в области видимости объемлющего модуля: 473 Типичные ошибки при работе с функциями »> ве1естог() тгасеьаск (щовт гесепт са!1 1авт): е>1е "<втбтп>", 1>пе ц тп о Ет!е "<втбтп>', 1!не 2, тп ве1естог апсоппбссоа!Еггог, !оса1 чагтаые 'Х' гетегепсеб Оетогв авюопщепт (бпаоппб(оса!Еггог обращение к локальной переменной 'Х' до присваивания) »> бе( ве1естог(): 91оаа1 Х рг1пт Х х = Ва И Принудительное обьиеление Х глобальнмм (везде) »> ве1естог() 99 При этом следует помнить, что в этом случае операция присваивания изменит глобальную переменную Х, а не локальную. Внутри функции можно использовать как локальную, так и глобальную версии одного и того же имени.
Если вы действительно предполагаете вывести значение глобальной переменной, а затем присвоить значение локальной версии того же самого имени, импортируйте вмещающий модуль и обращайтесь к глобальной переменной как к атрибуту модуля: >»Х=99 »> бе( ве1естог(): 1щрогт ща1п б ямлортировать вмещающий модуль Было получено сообщение о том, что переменная не определена, но причина его появления неочевидна. Этот программный код компилируется интерпретатором во время ввода в интерактивной оболочке или во время импорта модуля. Во время компиляции Ру(Ьоп обнаруживает операцию присваивания переменной Х и делает вывод, что Х вЂ” это локальное имя везде в теле функции.
Но во время выполнения функции, из-за того что к моменту вызова инструкции рг(пт операция присваивания еще не производилась, интерпретатор сообщает о том, что имя не определено. Согласно этому правилу использования имен он говорит, что обращение к локальной переменной Х произведено до того, как ей было присвоено значение. Фактически, любая операция присваивания внутри функции создает локальное имя. Операция импортирования, =, вложенные инструкции бег, вложенные определения классов и т. д., — все трактуются именно таким образом.
Проблема возникает из-за того, что операция присваивания делает имена локальными для всей функции, а не только для той ее части, которая следует за инструкцией присваивания. На самом деле предыдущий пример далеко не однозначен: что имелось в виду — требовалось вывести глобальную переменную Х и затем создать локальную переменную или это просто ошибка программиста2 Так как Ру(Ьоп интерпретирует имя Х как локальное во всей функции, то это ошибка — если вы действительно хотите вывести значение глобальной переменной Х, объявите ее глобальной с помощью инструкции 9!о ба1: 474 № Квалифицированное обравение № к глобальной версии ииени № Неквалифицированное локальное иия Х № Вывести локальную версии ииени рг1пт ва[п .Х Х=88 рг1пт Х »> ве1естсг() 99 88 Обращение по квалифицированному (полному) имени (с . Х) приводит к извлечению значения из пространства имен объекта.
Пространством имен интерактивной оболочки является модуль с именем пз!п, поэтому при обращении по имени аа1п . Х извлекается глобальная версия Х. Если что-то вам показалось непонятным, прочитайте пятую часть книги.' Значения по умолчанию и изменяемые объекты Значения по умолчанию для аргументов функции вычисляются и запоминаются в момент выполнения инструкции бег, а не при вызове функции. Внутренняя реализация Ру1Ьоп сохраняет по одному объекту для каждого аргумента со значением по умолчанию, присоединенного к функции. Вычисление значений по умолчанию в момент определения функции позволяет„в случае необходимости, сохранять значения из объемлющей области видимости.
Но, так как значения по умолчанию сохраняются между вызовами функции, следует быть внимательным при воздействии на изменяемые значения по умолчанию. Например, следующая функция использует пустой список в качестве значения по умолчанию своего аргумента, а затем изменяет его при каждом вызове: »> Ее№ зачет(х=[]): № Объект списка сохраняется х.аррепа(1) № Ври каждои вызове изиеняется один и тот же обьект.' рг1п1 х № Значение по уиолчании не используется № Используется значение по уиолчаниа № Список растет при каждои вызовет Положение дел с локальными переменными в нзыке Рус]топ к настоящему моменту несколько улучшилось, потому что в данном случае выводится более определенное сообщение об ошибке ч обращение к локальной переменной до присваивания>, которое показано в листинге примера (теперь оно используется вместо более расплывчатого сообщения об ошибке, свнзанной с именем), впрочем, этот вид ошибки все еще встречается.