Математический анализ. 3е издание
Скачать 4.86 Mb.
|
404 Глава 16. Области видимости и аргументы Разделение имен на глобальные и локальные также облегчает понима ние функций, так как большинство имен, используемых в функции, появляются непосредственно в самой функции, а не в какомто дру гом, произвольном месте внутри модуля. Кроме того, можно быть уве ренным, что локальные имена не будут изменены любой другой уда ленной функцией в программе, а это в свою очередь упрощает отладку программ. Встроенная область видимости Встроенная область видимости, уже упоминавшаяся выше, немного проще, чем можно было бы подумать. В действительности, встроенная область видимости – это всего лишь встроенный модуль с именем __builtin__ , но для того, чтобы использовать имя builtin, необходимо импортировать модуль __builtin__, потому что это имя само по себе не является встроенным. Я вполне серьезен! Встроенная область видимости реализована как мо дуль стандартной библиотеки с именем __builtin__, но само имя не на ходится во встроенной области видимости, поэтому, чтобы исследо вать его, необходимо импортировать модуль. После этого можно будет воспользоваться функцией dir, чтобы получить список предопреде ленных имен: >>> import __builtin__ >>> dir(__builtin__) ['ArithmeticError', 'AssertionError', 'AttributeError', 'DeprecationWarning', 'EOFError', 'Ellipsis', ...множество других имен опущено... 'str', 'super', 'tuple', 'type', 'unichr', 'unicode', 'vars', 'xrange', 'zip'] Имена в этом списке составляют встроенную область видимости языка Python. Примерно первая половина списка – это встроенные исключе ния, а вторая – встроенные функции. Согласно правилу LEGB интер претатор выполняет поиск имен в этом модуле в последнюю очередь. Все имена из этого списка вы получаете в свое распоряжение по умол чанию, т. е., чтобы их использовать, не требуется импортировать ка киелибо модули. Благодаря этому существует два способа вызвать встроенную функцию – используя правило LEGB или импортируя мо дуль __builtin__ вручную: >>> zip # Обычный способ <встроенная функция zip> >>> import __builtin__ # Более сложный способ >>> __builtin__.zip <встроенная функция zip> Второй способ иногда удобно использовать при выполнении сложных действий. Внимательный читатель может заметить, что согласно пра вилу LEGB поиск имени прекращается, когда будет найдено первое Правила видимости 405 подходящее имя, откуда следует, что имена в локальной области види мости могут переопределять переменные с теми же самыми именами, как в глобальной, так и во встроенной области видимости, а глобальные имена могут переопределять имена во встроенной области видимости. Например, внутри функции можно создать переменную с именем open: def hider(): open = 'spam' # Локальная переменная, переопределяет встроенное имя open('data.txt') # В этой области видимости файл не будет открыт! Однако в результате этого встроенная функция с именем open, которая располагается во встроенной области видимости, окажется скрытой. Обычно это считается ошибкой, и самое неприятное, что интерпретатор Python не выведет сообщения с предупреждением (иногда в програм мировании возникают ситуации, когда действительно бывает необхо димо подменить встроенные имена, переопределив их в своем коде). 1 Таким же способом функции могут переопределять имена глобальных переменных, определяя локальные переменные с теми же именами: X = 88 # Глобальная переменная X def func(): X = 99 # Локальная переменная X: переопределяет глобальную func() print X # Выведет 88: значение не изменилось В этом примере операция присваивания создает локальную перемен ную X, которая совершенно отлична от глобальной переменной X, опре деленной в модуле, за пределами функции. Вследствие этого внутри функции нет никакой возможности изменить переменную, располо женную за пределами функции, если не добавить объявление global в инструкцию def (как описано в следующем разделе). 1 Вот вам еще один пример того, что допустимо в языке Python, но чего не следует делать: имена True и False – это всего лишь переменные во встроен ной области видимости и вполне возможно их переопределить с помощью такой инструкции: True = False. При этом вы не нарушите общую логиче скую целостность! Эта инструкция всего лишь переопределит значение сло ва True в единственной области видимости. Что еще интереснее, можно вы полнить даже такую инструкцию: __builtin__.True = False, и тогда истина станет ложью для всей программы! Такая возможность в будущем, скорее всего, будет ликвидирована (кстати, она отправляет среду IDLE в странное состояние, когда пользовательский процесс сбрасывается). Однако такой прием удобен для создателей инструментальных средств, которые вынуж дены переопределять встроенные имена, такие как open, для нужд специа лизированных функций. Кроме того, следует отметить, что инструменты сторонних производителей, такие как PyChecker, выводят предупрежде ния о типичных ошибках программирования, включая случайное пере определение встроенных имен (эта возможность, встроенная в PyChecker, известна как «shadowing» (сокрытие)). 406 Глава 16. Области видимости и аргументы Инструкция global Инструкция global – это единственное, что в языке Python отдаленно напоминает инструкцию объявления. Однако это не объявление типа или размера – это объявление пространства имен. Она сообщает интер претатору, что функция будет изменять одно или более глобальных имен, т. е. имен, которые находятся в области видимости (в простран стве имен) вмещающего модуля. Инструкция global уже упоминалась выше, а ниже приводится общая информация о ней: • Глобальные имена – это имена, которые определены на верхнем уровне вмещающего модуля. • Глобальные имена должны объявляться, только если им будут при сваиваться значения внутри функций. • Обращаться к глобальным именам внутри функций можно и без объявления их глобальными. Инструкция global состоит из слова global и следующих за ним одного или более имен, разделенных запятыми, которые будут отображены на область видимости вмещающего модуля при обращении к ним или при выполнении операции присваивания внутри тела функции. Например: X = 88 # Глобальная переменная X def func(): global X X = 99 # Глобальная переменная X: за пределами инструкции def func() print X # Выведет 99 В этом примере было добавлено объявление global, поэтому имя X внут ри инструкции def теперь ссылается на переменную X за ее пределами. На этот раз оба имени представляют одну и ту же переменную. Ниже приводится более сложный пример использования инструкции global: y, z = 1, 2 # Глобальные переменные в модуле def all_global(): global x # Объявляется глобальной для присваивания x = y + z # Объявлять y, z не требуется: применяется правило LEGB Здесь все три переменные x, y и z, используемые внутри функции all_global , являются глобальными. Переменные y и z глобальными считаются потому, что внутри функции им не присваиваются значе ния. Переменная x считается глобальной потому, что она перечислена в инструкции global, которая явно отображает ее в область видимости модуля. Без инструкции global переменная x считалась бы локальной, так как ей присваивается значение внутри функции. Обратите внимание: переменные y и z не были объявлены как глобаль ные, однако, следуя правилу LEGB, интерпретатор автоматически отыщет их в области видимости модуля. Кроме того, следует отметить, Инструкция global 407 что переменная x может не существовать в модуле на момент вызова функции – в этом случае операция присваивания в функции создаст переменную x в области видимости модуля. Минимизируйте количество глобальных переменных По умолчанию имена, значения которым присваиваются внутри функ ций, являются локальными, поэтому, если необходимо изменять име на за пределами функций, необходимо использовать инструкцию glo bal . Это сделано в соответствии с общей идеологией языка Python – чтобы сделать чтото «неправильное», необходимо писать дополни тельный программный код. Иногда бывает удобно использовать гло бальные переменные, однако по умолчанию, если переменной при сваивается значение внутри инструкции def, она становится локаль ной, потому что это, как правило, наилучшее решение. Изменение глобальных переменных может привести к появлению проблем, хоро шо известных в разработке программного обеспечения: когда значе ния переменных зависят от порядка, в каком вызываются функции, это может осложнить отладку программы. Рассмотрим следующий пример модуля: X = 99 def func1(): global X X = 88 def func2(): global X X = 77 Теперь представим, что перед нами стоит задача модифицировать этот модуль или использовать его в другой программе. Каким будет значе ние переменной X? На самом деле этот вопрос не имеет смысла, если не указывать момент времени – значение переменной X зависит от вы бранного момента времени, так как оно зависит от того, какая функ ция вызывалась последней (этого нельзя сказать только по одному файлу модуля). В результате, чтобы понять этот программный код, необходимо знать путь потока выполнения всей программы. И если возникнет необходи мость изменить этот модуль или использовать его в другой программе, необходимо будет удерживать в своей памяти всю программу. В этой си туации невозможно использовать одну функцию, не принимая во вни мание другую. От них зависит значение глобальной переменной. Это типичная проблема глобальных переменных – они вообще делают про граммный код более сложным для понимания и использования, в отли чие от кода, состоящего только из независимых функций, логика вы полнения которых построена на использовании локальных имен. 408 Глава 16. Области видимости и аргументы С другой стороны, за исключением случаев использования классов и принципов объектноориентированного программирования, глобаль ные переменные являются едва ли не самым удобным способом хране ния информации о состоянии (информации, которую необходимо хра нить между вызовами функции) – локальные переменные исчезают, когда функция возвращает управление, а глобальные – нет. Это мож но реализовать с помощью других приемов, таких как использование изменяемых аргументов по умолчанию и области видимости объемлю щих функций, но они слишком сложны по сравнению с глобальными переменными. Некоторые программы определяют отдельный глобальный модуль для хранения всех глобальных имен – если это предусмотрено заранее, это не так вредно. Кроме того, программы на языке Python, использую щие многопоточную модель выполнения для параллельной обработки данных, тесно связаны с глобальными переменными – они играют роль памяти, совместно используемой функциями, исполняющимися в параллельных потоках, и выступают в качестве средств связи (опи сание многопоточной модели выполнения выходит далеко за рамки данной книги, поэтому за дополнительной информацией обращайтесь к книгам, упомянутым в предисловии). А пока, особенно если вы не имеете достаточного опыта программиро вания, по мере возможности избегайте искушения использовать гло бальные переменные (старайтесь организовать обмен данными через параметры и возвращаемые значения). Шесть месяцев спустя вы и ва ши коллеги будете рады, что поступали таким образом. Минимизируйте количество изменений в соседних файлах В этом разделе описывается еще одна проблема, связанная с областя ми видимости: несмотря на то, что существует возможность непо средственно изменять переменные в другом файле, этого следует избе гать. Рассмотрим следующие два модуля: # first.py X = 99 # second.py import first first.X = 88 Первый модуль определяет переменную X, а второй – изменяет ее зна чение в инструкции присваивания. Обратите внимание, что для этого во втором модуле необходимо импортировать первый модуль. Как вы уже знаете, каждый модуль представляет собой отдельное пространст во имен (где размещаются переменные), поэтому, чтобы увидеть содер жимое одного модуля во втором, его необходимо импортировать. В тер минах этой главы глобальная область видимости модуля после импор Инструкция global 409 тирования превращается в пространство имен атрибутов объекта мо дуля – импортирующий модуль автоматически получает доступ ко всем глобальным переменным импортируемого модуля, поэтому при импортировании глобальная область видимости модуля, по сути, трансформируется в пространство имен атрибутов. После импортирования первого модуля второй модуль присваивает его переменной новое значение. Проблема состоит в том, что эта операция выполняется слишком неявно: для любого, кто занимается сопровож дением или использует первый модуль, будет сложно догадаться, что какойто другой модуль, далеко отстоящий в цепочке импорта, может изменить значение переменной X. В конце концов, второй модуль мо жет находиться вообще в другом каталоге, изза чего его сложно будет найти. Здесь также устанавливается слишком тесная зависимость ме жду этими двумя модулями, потому что оба они зависят от значения переменной X, будет трудно понять или повторно использовать один модуль без другого. И этом случае лучшая рекомендация – не использовать такую возмож ность; лучше организовать взаимодействие между модулями через вы зовы функций, передавая им аргументы и получая возвращаемые зна чения. В данном конкретном случае было бы лучше добавить функ цию доступа, которая будет выполнять изменения: # first.py X = 99 def setX(new): global X X = new # second.py import first first.setX(88) Для этого потребуется добавить дополнительный программный код, но он имеет огромное значение в смысле обеспечения удобочитаемости и удобства в сопровождении – когда тот, кто впервые будет знакомить ся с модулем, увидит функцию, он будет знать, что это часть интер фейса модуля и поймет, что переменная X может изменяться. Мы не можем полностью избавиться от изменений в соседних файлах, однако здравый смысл диктует необходимость минимизировать их число, ес ли это не является широко распространенным явлением в программе. Другие способы доступа к глобальным переменным Интересно, что благодаря трансформации глобальных переменных в ат рибуты объекта загруженного модуля существует возможность имити ровать инструкцию global, импортируя вмещающий модуль и выпол няя присваивание его атрибутам, как показано в следующем примере модуля. Программный код в этом файле в одном случае импортирует вмещающий модуль по имени, а в другом использует таблицу загру 410 Глава 16. Области видимости и аргументы женных модулей sys.modules (подробнее об этой таблице рассказывает ся в главе 21): # thismod.py var = 99 # Глобальная переменная == атрибут модуля def local(): var = 0 # Изменяется локальная переменная def glob1(): global var # Глобальное объявление (обычное) var += 1 # Изменяется глобальная переменная def glob2(): var = 0 # Изменяется локальная переменная import thismod # Импорт самого себя thismod.var += 1 # Изменяется глобальная переменная def glob3(): var = 0 # Изменяется локальная переменная import sys # Импорт системной таблицы glob = sys.modules['thismod'] # Получить объект модуля # (или использовать __name__) glob.var += 1 # Изменяется глобальная переменная def test(): print var local(); glob1(); glob2(); glob3() print var После запуска будут добавлены 3 глобальные переменные (только пер вая функция ничего не добавляет): >>> import thismod >>> thismod.test() 99 102 >>> thismod.var 102 Этот пример иллюстрирует эквивалентность глобальных имен и атри бутов модуля, однако, чтобы явно выразить свои намерения, нам по требовалось написать немного больше, чем при использовании инст рукции global. Области видимости и вложенные функции До сих пор мы не еще рассматривали одну часть правила области види мости в языке Python (просто потому, что с нею редко сталкиваются на практике). Однако пришло время более пристально посмотреть на E в правиле LEGB. Уровень E появился относительно недавно (он был добавлен в Python 2.2) – это локальные области видимости объемлю щих инструкций def. Иногда объемлющие области видимости называ Области видимости и вложенные функции 411 ют статически вложенными областями видимости. В действитель ности вложение является лексическим – вложенные области видимо сти соответствуют физически вложенным блокам программного кода в исходных текстах программы. В Python 3.0 предполагается появление инструкции nonlocal, ко торая позволит получать доступ на запись к переменным в облас тях видимости объемлющих функций, так же, как нынешняя инструкция global позволяет получить доступ на запись к облас ти видимости объемлющего модуля. Синтаксически эта инструк ция будет выглядеть так же, как инструкция global, только в ней будет использоваться слово nonlocal. Однако это в будущем, по этому дополнительную информацию ищите в примечаниях к вы пуску 3.0. Вложенные области видимости С появлением областей видимости вложенных функций правила поис ка переменных стали немного более сложными. Внутри функции: • Операция присваивания (X = value) создает или изменяет имя X в те кущей локальной области видимости по умолчанию. Если имя X объ явлено глобальным внутри функции, операция присваивания созда ет или изменяет имя X в области видимости объемлющего модуля. • При обращении к переменной (X) поиск имени X сначала произво дится в локальной области видимости (функции); затем в локаль ных областях видимости всех лексически объемлющих функций, изнутри наружу; затем в текущей глобальной области видимости (в модуле); и, наконец, во встроенной области видимости (модуль __builtin__ ). Поиск имен, объявленных в инструкции global, начи нается сразу с глобальной области видимости. Обратите внимание, что инструкция global отображает имена в область видимости объемлющего модуля. Когда имеются вложенные функ ции, можно получить значения переменных в объемлющих функциях, но их нельзя изменить. Чтобы пояснить эти положения, рассмотрим их на примере программного кода. |