Как обратиться к глобальной переменной в python
Перейти к содержимому

Как обратиться к глобальной переменной в python

  • автор:

Все, что вы хотели узнать про области видимости в Python, но стеснялись спросить

Сегодня мы будем говорить о важных теоретических основах, которые необходимо понимать и помнить, чтобы писать грамотный, читаемый и красивый код. Мы будем вести речь об областях видимости переменных. Эта статья будет полезна не только новичкам, но и опытным программистам, которые пришли в Python из другого языка и хотят разобраться с его механиками работы.

Области видимости определяют, в какой части программы мы можем работать с той или иной переменной, а от каких переменная «скрыта». Крайне важно понимать, как использовать только те значения и переменные, которые нам нужны, и как интерпретатор языка себя при этом ведет. А еще мы посмотрим, как обходить ограничения, накладываемые областями видимости на действия с переменными. В Python существует целых 3 области видимости:

  • Локальная
  • Глобальная
  • Нелокальная

Обычно, речь заходит про области видимости, когда происходит знакомство с функциями. На их примере мы и будем рассматривать работу областей видимости переменных.

Локальная область видимости

Рассмотрим функцию, которая выведет список some_list поэлементно:

Здесь element и some_list – локальные переменные, которые видны только внутри функции, и которые не могут использоваться за ее пределами с теми значениями, которые были им присвоены внутри функции при ее работе. То есть, если мы в основном теле программы вызовем print(element) , то получим ошибку:

Теперь мы поступим следующим образом:

Здесь переменная element внутри функции и переменная с таким же именем вне ее – это две разные переменные, их значения не перекрещиваются и не взаимозаменяются. Они называются одинаково, но ссылаются на разные объекты в памяти. Более того, переменная с именем element внутри функции живет столько же, сколько выполняется функция и не больше. Но будьте аккуратны с тем, чтобы давать локальным и глобальным переменным одинаковые имена, сейчас покажу почему:

Обратите внимание на то, что интерпретатор не указал нам на ошибки. А все потому что sudden_list находится в глобальной области видимости, то есть изнутри функции print_list мы можем к нему обращаться, поскольку изнутри видно то, что происходит снаружи. По причине таких механик работы старайтесь называть локальные переменные внутри функции не так, как называете переменные в глобальной области видимости.

Здесь важно поговорить о константах. Интерпретатору Python нет разницы как вы называете переменную, поэтому код выше будет лучше переписать в следующем виде:

Теперь все на своих местах. Дело в том, что в Python нельзя каким-то образом строго определить константу, как объект, который не должен быть изменен. Так что то, как вы используете значение переменной, имя которой записано заглавными буквами, остается лишь на вашей совести. Другой вопрос, что таким способом записанная переменная даст понять тому, кто будет читать ваш код, что переменная нигде изменяться не будет. Или по крайней мере не должна.

Глобальная область видимости

В Python есть ключевое слово global , которое позволяет изменять изнутри функции значение глобальной переменной. Оно записывается перед именем переменной, которая дальше внутри функции будет считаться глобальной. Как видно из примера, теперь значение переменной candy увеличивается, и обратите внимание на то, что мы не передаем ее в качестве аргумента функции get_candy() .

В результате получим:

Однако менять значение глобальной переменной изнутри функции – не лучшая практика и лучше так не делать, поскольку читаемости кода это не способствует. Чем меньше то, что происходит внутри функции будет зависеть от глобальной области видимости, тем лучше.

Лайфхак: Чтобы не мучиться с именованием переменных, вы можете вынести основной код программы в функцию main() , тогда все переменные, которые будут объявлены внутри этой функции останутся локальными и не будут портить глобальную область видимости, увеличивая вероятность допустить ошибку.

Нелокальная область видимости

Появилось это понятие в Python 3 вместе с ключевым словом nonlocal . Логика его написания примерно такая же, как и у global . Однако у nonlocal есть особенность. Nonlocal используется чаще всего во вложенных функциях, когда мы хотим дать интерпретатору понять, что для вложенной функции определенная переменная не является локальной, но она и не является глобальной в общем смысле.

Насколько это полезно вам предстоит решить самостоятельно. Больше примеров вы можете найти здесь.

В качестве вывода можно сформулировать несколько правил:

  1. Изнутри функции видны переменные, которые были определены и внутри нее и снаружи. Переменные, определенные внутри – локальные, снаружи – глобальные.
  2. Снаружи функций не видны никакие переменные, определенные внутри них.
  3. Изнутри функции можно изменять значение переменных, которые определены в глобальной области видимости с помощью спецификатора global .
  4. Изнутри вложенной функции с помощью спецификатора nonlocal можно изменять значения переменных, которые были определены во внешней функции, но не находятся в глобальной области видимости.

Также хочу пригласить всех желающих на бесплатный вебинар от OTUS, где изучим такой инструмент, как аннотация типов в Python: обсудим причины, по которым его многие недооценивают, рассмотрим ряд примеров из боевой практики, когда аннотация типов могла спасти или спасла ситуацию. Поговорим о том, как и когда внедрять проверку типов на своих проектах.

Пространства имен и области видимости в Python

Jenny V

Данная статья посвящена теме пространств имен в Python. Это структуры, которые используются для организации символических имен, присваиваемых объектам в программе.

В Python понятие объекта является ключевым. Они везде! Фактически все, что программа Python создает или с чем работает, — это объект.

Выражение присваивания создает символическое имя, которое вы можете использовать для ссылки на объект. Так выражение x = 'foo' создает символическое имя x , которое ссылается на строковый объект 'foo' .

В более сложных программах вам предстоит создавать сотни или тысячи подобных имен, указывающих на конкретные объекты. Как же Python отслеживает все эти имена и предотвращает их путаницу?

Изучив данную статью, вы узнаете:

  • как Python структурирует символические имена и объекты в пространствах имен;
  • когда Python создает новое пространство имен;
  • как осуществляется реализация пространств имен;
  • как область переменной определяет видимость символических имен.

Пространства имен в Python

Пространство имен — это совокупность определенных в настоящий момент символических имен и информации об объектах, на которые они ссылаются. Вы можете рассматривать такое пространство как словарь, в котором ключи являются именами объектов, а значения — самими объектами. Каждая пара ключ-значение соотносит имя с соответствующим ему объектом.

Пространства имен — отличная штука! Будем использовать их чаще! — Тим Петерс в “Дзен Python”.

Как утверждает Тим Петерс, пространства имен — отличная штука, которую активно использует Python. Существует 4 типа пространств имен:

  1. Встроенное.
  2. Глобальное.
  3. Объемлющее.
  4. Локальное.

Они обладают разными жизненными циклами. По мере выполнения программы Python создает необходимые пространства имен и удаляет их, когда потребность в них пропадает. Как правило, в любой момент времени существует множество пространств имен.

Встроенное пространство имен

Встроенное пространство имен содержит имена всех встроенных объектов, которые всегда доступны при работе в Python. Вы можете перечислить объекты во встроенном пространстве с помощью следующей команды:

Перечень включает, например, исключение StopIteration , такие встроенные функции, как max() и len() , а также типы объектов — int и str .

При запуске интерпретатор Python создает встроенное пространство имен. Оно сохраняется до тех пор, пока интерпретатор не завершит работу.

Глобальное пространство имен

Глобальное пространство имен содержит имена, определенные на уровне основной программы, и создаётся сразу при запуске тела этой программы. Сохраняется же оно до момента завершения работы интерпретатора.

Строго говоря, могут существовать и другие глобальные пространства имен. Интерпретатор также создает пространство данного типа для любого модуля, загружаемого программой при помощи выражения import .

Теперь, встречая понятие “глобальное пространство имен”, вы будете знать, что оно принадлежит основной программе.

Локальное и объемлющее пространства имен

Интерпретатор создает новое пространство имен при каждом выполнении функции. Это пространство является локальным для функции и сохраняется до момента завершения ее действия.

Функции не существуют независимо друг от друга только на уровне основной программы. Вы также можете определять одну функцию внутри другой.

В этом примере функция g() определена внутри тела f() . Вот что происходит в данном коде:

  • Строки с 1 по 12 определяют f() , объемлющую функцию.
  • Строки с 4 по 7 определяют g() , вложенную функцию.
  • В строке 15 основная программа вызывает f() .
  • В строке 9 f() вызывает g() .

Когда основная программа вызывает f() , Python создает для нее новое пространство имен. Аналогичным образом, когда f() вызывает g() , последняя получает свое собственное отдельное пространство. Пространство, созданное для g() , является локальным, а пространство, созданное для f() , — объемлющим.

Все эти пространства существуют до тех пор, пока выполняются соответствующие им функции. По завершении же этих функций Python может не сразу отозвать их из памяти, но при этом все ссылки на содержащиеся в них объекты сразу становятся недоступными.

Область видимости переменной

Наличие нескольких отличных пространств имен означает, что в процессе выполнения программы Python несколько разных экземпляров одного имени могут существовать одновременно. Пока каждый из них находится в собственном пространстве, все они обслуживаются по отдельности, и путаницы не происходит.

Но тут возникает вопрос. Предположим, что вы ссылаетесь на имя x в коде, а оно существует в нескольких пространствах. Как Python узнает, какое именно вы имеете в виду?

Ответ кроется в понятии области видимости имени, представляющей из себя часть программы, в которой данное имя обладает значением. Интерпретатор определяет эту область в среде выполнения, основываясь на том, где располагается определение имени и из какого места в коде на него ссылаются.

С более детальной информацией об области видимости в программировании вы можете ознакомиться на соответствующей странице Википедии.

Отвечая на заданный выше вопрос, отметим, что если ваш код ссылается на имя x , то Python будет искать его следующих областях видимости в таком порядке:

  1. Локальная. Если вы ссылаетесь на x внутри функции, то интерпретатор сначала ищет его в самой внутренней области, локальной для этой функции.
  2. Объемлющая. Если x не находится в локальной области, но появляется в функции, располагающейся внутри другой функции, то интерпретатор ищет его в области видимости объемлющей функции.
  3. Глобальная. Если ни один из вышеуказанных вариантов не принес результатов, то интерпретатор продолжит поиск в глобальной области видимости.
  4. Встроенная. Если интерпретатор не может найти x где-либо еще, то он направляет поиски во встроенную область видимости.

Эта последовательность составляет суть правила областей видимости LEGB, как его обычно называют в публикациях о Python (хотя, на самом деле, данный термин не встречается в его официальной документации). Интерпретатор начинает поиски имени изнутри, последовательно переходя от локальной области видимости к объемлющей, затем к глобальной и в завершении к встроенной.

Если интерпретатор не находит имя ни в одной из этих областей, то Python вызывает исключение NameError .

Примеры

Ниже представлен ряд примеров с правилом LEGB. В каждом из них самая внутренняя вложенная функция g() пытается вывести в консоль значение переменной с именем x . Обратите внимание, как в каждом примере происходит вывод разного значения x в зависимости от области видимости.

Пример 1. Одно определение

В этом примере имя x определено только в одной области. Оно находится за пределами функций f() и g() и поэтому относится к глобальной области видимости.

Выражение print() в строке 6 может ссылаться только на одно возможное имя x . Оно отображает объект x , определенный в глобальном пространстве имен, которым является строка 'global' .

Пример 2. Двойное определение

В следующем примере определение x появляется в двух местах: одно — вне f() и другое — внутри f() , но за пределами g() .

Как и в предыдущем примере g() ссылается на x . Но на этот раз предполагается выбор из двух определений:

  • Строка 1 определяет x в глобальной области видимости.
  • Строка 4 определяет x снова в объемлющей области видимости.

Согласно правилу LEGB интерпретатор находит значение в объемлющей области перед тем, как искать в глобальной. Поэтому выражение print() в строке 7 отображает 'enclosing' вместо 'global' .

Пример 3. Тройное определение

Теперь рассмотрим ситуацию, в которой x определен везде и всюду. Одно определение находится вне f() , другое — внутри f() , но за пределами g() , а третье — внутри g() .

Теперь выражение print() в строке 8 должно выбрать из трех возможных вариантов:

  • Строка 1 определяет x в глобальной области видимости.
  • Строка 4 определяет x в объемлющей области видимости.
  • Строка 7 определяет x в третий раз в локальной области g() .

В данном случае правило LEGB утверждает, что g() сначала видит свое собственное значение x , определенное в локальной области видимости. Поэтому выражение print() отображает 'local' .

Пример 4. Отсутствие определения

В заключительном примере рассмотрим случай, в котором g() пытается вывести значение x , но x нигде не определен, поэтому мы получим ошибку.

На этот раз Python не находит x ни в одном из пространств имен, поэтому выражение print() в строке 4 выдает исключение NameError .

Словари пространств имен Python

В первом разделе мы уже рекомендовали вам рассматривать пространство имен как словарь, в котором ключи — это имена объектов, а значения — сами объекты. По сути, для глобальных и локальных пространств они именно таковыми и являются! Python действительно реализует их как словари.

Примечание. Встроенное пространство работает не как словарь. Python реализует его как модуль.

Python предоставляет встроенные функции globals() и locals() , обеспечивающие доступ к глобальным и локальным словарям пространств имен.

Функция globals()

Встроенная функция globals() возвращает ссылку на текущий словарь глобального пространства имен. Ее можно использовать для обращения к объектам в этом пространстве. Посмотрим, как это будет выглядеть при запуске основной программы.

Как видно из примера, интерпретатор уже поместил ряд записей в globals() . В зависимости от версии Python и операционной системы в вашей среде это может выглядеть несколько иначе, но в целом все равно будет похоже.

Теперь посмотрим, что происходит при определении переменной в глобальной области видимости.

Вслед за выражением присваивания x = 'foo' в словаре глобального пространства имен появляется новый элемент. Ключ словаря — это имя объекта, т. е. x , а его значение — значение объекта, а именно 'foo' .

Как правило, вы обращаетесь к этому объекту обычным способом, ссылаясь на его символическое имя x . Но можно получить к нему доступ косвенным путем посредством словаря глобального пространства имен.

Оператор проверки типов is в строке 6 подтверждает, что в действительности это один и тот же объект.

Вы можете создавать и изменять записи в глобальном пространстве имен, также используя функцию globals() .

Выражение в строке 1 равнозначно выражению y = 100 , а в строке 12 — выражению y = 3.14159 .

Этот способ создания и изменения объектов в глобальной области видимости выглядит немного оригинальным, учитывая, что с этой задачей справятся и простые выражения присваивания. Но он работает и прекрасно отражает идею.

Функция locals()

Pyhton также предоставляет соответствующую встроенную функцию locals() . Она похожа на globals() , но отличается от нее тем, что обращается к объектам в локальном пространстве имен.

Когда locals() вызывается внутри f() , она возвращает словарь, представляющий локальное пространство имен функции. Обратите внимание, что помимо локально определенной переменной s это пространство включает параметры функций x и y , поскольку они также являются локальными для f() .

Если вы вызываете locals() за пределами функции в основой программе, то она ведет себя так же как и globals() .

Между globals() и locals() существует небольшое отличие, о котором вам будет полезно узнать.

globals() возвращает актуальную ссылку на словарь, содержащий глобальное пространство имен. Это значит, что если вы вызываете globals() , сохраняете возвращаемое значение и после этого определяете дополнительные переменные, то эти новые переменные появятся в словаре, на который указывает сохраненное возвращаемое значение.

В этом примере g является ссылкой на словарь глобального пространства имен. После выражений присваивания в строках 8 и 9 x и y появляются в словаре, на который указывает g .

В свою очередь, locals() возвращает словарь, являющийся текущей копией локального пространства имен, а не ссылкой на него. Дальнейшие дополнения к локальному пространству не повлияют на предыдущее возвращаемое значение locals() до момента ее повторного вызова. Кроме того, вы не можете изменять объекты в текущем локальном пространстве имен, используя возвращаемое значение locals() .

В этом примере loc указывает на возвращаемое значение locals() , являющееся копией локального пространства. Выражение x = 20 в строке 6 добавляет x в локальное пространство, а не в копию, на которую указывает loc . Аналогично этому, выражение в строке 9 изменяет значение для ключа 's' в копии, на которую указывает loc , но это никак не влияет на значение s в текущем локальном пространстве имен.

Это едва уловимое отличие может доставить вам хлопот, если вы его не запомните.

Изменение переменных вне области видимости

Как вам известно, передача аргументов функции в Python может проходить двумя способами: по значению и по ссылке. Иногда функция может изменить свой аргумент в среде вызова, внося коррективы в соответствующий параметр, а иногда у нее такой возможности нет:

  • Неизменяемый аргумент никогда не может быть изменен функцией.
  • Изменяемый аргумент нельзя переопределять целиком, но зато можно изменять.

Похожая ситуация возникает, когда функция пытается изменить переменную вне своей локальной области, ведь изменение неизменяемого объекта вне ее границ ей недоступно.

Когда f() выполняет выражение присваивания x = 40 в строке 3, она создает новую локальную ссылку на объект целого числа со значением 40 . На этом этапе f() теряет ссылку на объект с именем x в глобальном пространстве имен. Таким образом, выражение присваивания не влияет на глобальный объект.

Примечание. Когда функция f() выполняет print(x) в строке 4, она отображает 40 , значение своей собственной локальной переменной x . Но после завершения действия f() значение x в глобальной области видимости по прежнему 20 .

Функция может скорректировать объект изменяемого типа, находящийся за пределами ее локальной области видимости, если изменит его внутри:

В этом случае my_list — это список, а списки являются изменяемыми типами данных. f() может вносить изменения внутрь my_list , даже если он находится вне локальной области видимости. Но если f() стремится полностью переназначить my_list , то она создаст новый локальный объект и не изменит глобальный my_list .

Это похоже на процесс, при котором f() стремится модифицировать изменяемый аргумент функции.

Объявление global

А что если вам действительно необходимо изменить значение в глобальной области видимости изнутри f() ? Python делает это возможным благодаря использованию объявления global .

Выражение global x указывает на то, что пока выполняется f() , ссылки на имя x будут вести к x , находящемуся в глобальном пространстве имен. Это значит, что присваивание x = 40 не создает новую ссылку. Вместо этого оно присваивает новое значение x в глобальной области видимости.

Как видите, globals() возвращает ссылку на словарь глобального пространства имен. При желании, вместо использования выражения global , можно было бы осуществить то же самое, применив globals() :

Но особых причин делать это таким способом у нас нет, поскольку объявление global , вероятно, точнее отражает наше намерение. Но тем не менее этот вариант позволяет продемонстрировать принцип работы globals() .

Если же имя, определенное в объявлении global , не существует в глобальной области при запуске функции, то его создаст комбинация выражений global и присваивания.

В этом примере при запуске g() в глобальной области нет объекта с именем y , но g() создаст его с помощью выражения global y в строке 8.

Вы также можете указать несколько имен, разделенных запятыми, в одном объявлении global .

Здесь x , y и z объявляются для ссылок на объекты в глобальной области видимости посредством одного выражения global в строке 4.

Имя, определенное в объявлении global , не может появиться в функции раньше выражения global .

Цель выражения global x в строке 3 состоит в том, чтобы ссылки на x вели к объекту в глобальной области видимости. Но выражение print() в строке 2 ссылается на x до объявления global , что приводит к выводу исключения SyntaxError .

Объявление nonlocal

Схожая ситуация наблюдается с определениями вложенных функций. Объявление global позволяет функции обращаться к объекту в глобальной области видимости и менять его. А что если вложенной функции необходимо изменить объект в объемлющей области? Рассмотрим пример:

В этом примере первое определение x дано в объемлющей области, а не в глобальной. Точно так же, как g() не может напрямую изменить переменную в глобальной области, она не способна изменить x в объемлющей области функции. После присваивания x = 40 в строке 5 x в объемлющей области остается 20 .

Ключевое слово global не способствует решению этой ситуации:

Поскольку x находится в объемлющей области функции, а не в глобальной, ключевое слово global здесь не сработает. После завершения действия g() значение x в объемлющей области остается 20 .

На самом деле, в этом примере выражение global x не только не предоставляет доступ к x в объемлющей области, но также создает объект с именем x в глобальной области со значением 40 .

Для модификации x в объемлющей области изнутри g() вам потребуется аналогичное ключевое слово nonlocal . Имена, определенные после nonlocal , ссылаются на переменные в ближайшей объемлющей области.

После выражения nonlocal x в строке 5, когда g() ссылается на x , оно обращается к x в ближайшей объемлющей области, чье определение дано внутри f() в строке 2.

Выражение print() в завершении f() в строке 9 подтверждает, что вызов g() изменил значение x в объемлющей области на 40 .

Лучшие практики

Несмотря на то, что Python предоставляет ключевые слова global и nonlocal , бывают ситуации, когда их использование не рекомендовано.

Когда функция меняет данные вне локальной области, используя ключевые слова ( global или nonlocal ) или напрямую преобразуя изменяемый тип внутри, то это своего рода побочный эффект, аналогичный ситуации с изменением функцией одного из своих аргументов. Частая модификация глобальных переменных обычно не приветствуется ни в Python, ни в других языках программирования.

Как и со многими другими аспектами жизни, это, скорее всего, дело вкуса. Бывают ситуации, когда разумный подход к изменению глобальных переменных помогает уменьшить сложность программы.

По крайней мере, использование ключевого слова global в Python явно свидетельствует об изменении функцией глобальной переменной. Во многих других языках функция может менять глобальную переменную только посредством присваивания, не объявляя об этом каким-либо образом. Вследствие чего становится довольно сложно отследить, где происходит изменение глобальных данных.

В общем, изменение переменных за пределами локальной области видимости обычно не требуется. Почти всегда существует лучший способ — обычно с возвращаемыми значениями функции.

Заключение

Фактически все, что программа Python использует или с чем работает, — это объект. Даже будучи короткой, она создаст много разных объектов. А в более сложной программе их количество исчисляется тысячами. Python должен отслеживать все эти объекты и их имена, и помогают ему в этом пространства имен.

Ключевое слово global в Python

В этой статье вы узнаете, для чего нужно ключевое слово global и когда его использовать. А еще мы подробнее поговорим о глобальных переменных.

Поэтому перед прочтением этой статьи ознакомьтесь со статьей «Глобальные, локальные и нелокальные переменные в Python».

Что это такое

Ключевое слово global позволяет изменять переменную вне текущей области видимости в Python. global используется для создания глобальной переменной и изменения ее в локальной области видимости.

Что нужно знать прежде, чем работать с global

  • Когда мы создаем переменную внутри функции, она по умолчанию является локальной.
  • Когда мы определяем переменную вне функции, она по умолчанию является глобальной. В этом случае не нужно использовать ключевое слово global .
  • Ключевое слово global нужно для получения доступа к глобальной переменной и изменения ее внутри функции, то есть внутри локальной области видимости.
  • Использовать ключевое слово global вне функции бессмысленно.

Как использовать global

Пример 1. Получаем доступ к глобальной переменной внутри функции

Вывод:

Все работает! Но что если нам нужно изменить глобальную переменную внутри функции?

Пример 2. Изменяем глобальную переменную внутри функции

Вывод:

Python сообщает нам, что мы использовали локальную переменную c до ее объявления. Дело в том, что получить доступ к глобальной переменной внутри функции мы можем, а вот изменять ее в локальной области видимости — уже нет.

Для этого и нужно ключевое слово global .

Пример 3. Изменяем глобальную переменную внутри функции с помощью global

Вывод:

В приведенной выше программе мы определяем c как глобальную переменную внутри функции add() с помощью ключевого слова `global`.

Внутри функции мы также увеличиваем переменную c ` на 2, то есть выполняем действие c = c + 2 . И, наконец, выводим на экран глобальную переменную c .

Как мы видим, глобальная переменная c изменилась не только внутри функции, но и в глобальной области видимости. И там, и там c = 2 .

Глобальные переменные в модулях Python

В Python принято создавать единый модуль config.py для хранения глобальных переменных и обмена информацией между модулями Python в одной программе.

Вот как можно использовать глобальные переменные между модулями Python.

Пример 4. Используем глобальную переменную между модулями

Создаем файл config.py , в нем будем хранить глобальные переменные.

Создаем файл update.py , чтобы изменять глобальные переменные.

Создаем файл main.py . Его будем использовать, чтобы отслеживать изменения значений глобальных переменных.

Теперь запустим файл main.py .

Вывод:

Итак, мы создали 3 файла: config.py , update.py , и main.py .

Модуль config.py хранит глобальные переменные a и b . В файле update.py мы импортируем модуль config.py и изменяем значения a и b . Точно так же в файл main.py мы импортируем config.py и update.py . Наконец, мы печатаем и проверяем значения глобальных переменных независимо от того, изменились они или нет.

Как использовать global во вложенных функция

Пример 5. Используем глобальные переменные во вложенных функциях

Вывод:

В приведенной выше программе мы объявили глобальную переменную внутри вложенной функции bar() . Внутри функции foo() ключевое слово global никак не влияет на переменную x .

До и после вызова bar() переменная x принимает значение локальной переменной, т. е. x = 20 . Вне функции foo() переменная x примет значение, определенное в функции x , т. е. x = 25 . Так произойдет потому, что мы использовали ключевое слово global , чтобы создать глобальную переменную x внутри функции bar() , то есть в локальной области видимости.

Если мы сделаем какие-либо изменения внутри функции bar() , изменения отобразятся и за пределами локальной области видимости, то есть за пределами функции foo() .

# Функции с параметрами

В программировании особе внимание уделяется концепции о локальных и глобальных переменных, а также связанное с ними представление об областях видимости. Соответственно, локальные переменные видны только в локальной области видимости, которой может выступать отдельно взятая функция. Глобальные переменные видны во всей программе. «Видны» – значит, известны, доступны. К ним можно обратиться по имени и получить связанное с ними значение.

К глобальной переменной можно обратиться из локальной области видимости. К локальной переменной нельзя обратиться из глобальной области видимости, потому что локальная переменная существует только в момент выполнения тела функции. При выходе из нее, локальные переменные исчезают. Компьютерная память, которая под них отводилась, освобождается. Когда функция будет снова вызвана, локальные переменные будут созданы заново.

Вернемся к нашей программе из прошлого урока, немного упростив ее для удобства:

Сколько здесь переменных? Какие из них являются глобальными, а какие – локальными?

Здесь пять переменных. Глобальной является только figure. Переменные a и b из функции rectangle() , а также a и h из triangle() – локальные. При этом локальные переменные с одним и тем же идентификатором a, но объявленные в разных функциях, – разные переменные.

Следует отметить, что идентификаторы rectangle и triangle , хотя и не являются именами переменных, а представляют собой имена функций, также имеют область видимости. В данном случае она глобальная, так как функции объявлены непосредственно в основной ветке программы.

В приведенной программе к глобальной области видимости относятся заголовки объявлений функций, объявление и присваивание переменной figure, конструкция условного оператора.

К локальной области относятся тела функций. Если, находясь в глобальной области видимости, мы попытаемся обратиться к локальной переменной, то возникнет ошибка. Добавьте в конце кода команду:

Однако мы можем обращаться из функций к глобальным переменным:

В данном случае из тел функций происходит обращение к имени figure, которое, из-за того, что было объявлено в глобальной области видимости, видимо во всей программе.

Наши функции не совсем идеальны. Они должны вычислять площади фигур, но выводить результат на экран им не следовало бы. Вполне вероятна ситуация, когда результат нужен для внутренних нужд программы, для каких-то дальнейших вычислений, а выводить ли его на экран – вопрос второстепенный.

Если функции не будут выводить, а только вычислять результат, то его надо где-то сохранить для дальнейшего использования. Для этого подошли бы глобальные переменные. В них можно записать результат. Напишем программу вот так:

Итак, мы ввели в программу глобальную переменную result и инициировали ее нулем. В функциях ей присваивается результат вычислений. В конце программы ее значение выводится на экран. Мы ожидаем, что программа будет прекрасно работать. Однакo:

… что-то пошло не так.

Дело в том, что в Python присвоение значения переменной совмещено с ее объявлением. (Во многих других языках это не так.) Поэтому, когда имя result впервые упоминается в локальной области видимости, и при этом происходит присваивание ей значения, то создается локальная переменная result . Это другая переменная, никак не связанная с глобальной result .

Когда функция завершает свою работу, то значение локальной result теряется, а глобальная не была изменена.

Когда мы вызывали внутри функции переменную figure, то ничего ей не присваивали. Наоборот, мы запрашивали ее значение. Интерпретатор Питона искал ее значение сначала в локальной области видимости и не находил. После этого шел в глобальную и находил.

В случае с result он ничего не ищет. Он выполняет вычисления справа от знака присваивания, создает локальную переменную result , связывает ее с полученным значением.

На самом деле можно принудительно обратиться к глобальной переменной. Для этого существует команда global :

В таком варианте программа будет работать правильно.

Однако менять значения глобальных переменных в теле функции – плохая практика. В больших программах программисту трудно отследить, где, какая функция и почему изменила их значение. Программист смотрит на исходное значение глобальной переменной и может подумать, что оно остается таким же. Сложно заметить, что какая-то функция поменяла его. Подобное ведет к логическим ошибкам.

Чтобы избавиться от необходимости использовать глобальные переменные, для функций существует возможность возврата результата своей работы в основную ветку программы. И уже это полученное из функции значение можно присвоить глобальной переменной в глобальной области видимости. Это делает программу более понятной.

Как функция принимает и возвращает данные, будет рассмотрено в следующих уроках.

# Упражнения

В языке Python можно внутри одной функции определять другую. Напишите программу по следующему описанию.

В основной ветке программы вызывается функция cylinder() , которая вычисляет площадь цилиндра. В теле cylinder() определена функция circle() , вычисляющая площадь круга по формуле πr2. В теле cylinder() у пользователя спрашивается, хочет ли он получить только площадь боковой поверхности цилиндра, которая вычисляется по формуле 2πrh, или полную площадь цилиндра. В последнем случае к площади боковой поверхности цилиндра должен добавляться удвоенный результат вычислений функции circle() .

Как вы думаете, можно ли из основной ветки программы вызвать функцию, вложенную в другую функцию? Почему?

# Возврат значений из функции. Оператор return

Функции могут передавать какие-либо данные из своих тел в основную ветку программы. Говорят, что функция возвращает значение. В большинстве языков программирования, в том числе Python, выход из функции и передача данных в то место, откуда она была вызвана, выполняется оператором return.

Если интерпретатор Питона, выполняя тело функции, встречает return, то он «забирает» значение, указанное после этой команды, и «уходит» из функции:

В данной программе в основную ветку из функции возвращается значение локальной переменной full . Не сама переменная, а ее значение, в данном случае – какое-либо число, полученное в результате вычисления площади цилиндра.

В основной ветке программы это значение присваивается глобальной переменной square. То есть выражение square = cylinder() выполняется так:

Вызывается функция cylinder() .

Из нее возвращается значение.

Это значение присваивается переменной square.

Не обязательно присваивать результат переменной, его можно сразу вывести на экран:

Здесь число, полученное из cylinder() , непосредственно передается функции print() . Если мы в программе просто напишем cylinder() , не присвоив полученные данные переменной или не передав их куда-либо дальше, то эти данные будут потеряны. Но синтаксической ошибки не будет.

В функции может быть несколько операторов return. Однако всегда выполняется только один из них. Тот, которого первым достигнет поток выполнения. Допустим, мы решили обработать исключение, возникающее на некорректный ввод. Пусть тогда в ветке except обработчика исключений происходит выход из функции без всяких вычислений и передачи значения:

Если попытаться вместо цифр ввести буквы, то сработает return, вложенный в except. Он завершит выполнение функции, так что все нижеследующие вычисления, в том числе return full, будут опущены. Пример выполнения:

Но постойте! Что это за слово None , которое нам вернул «пустой» return? Это ничего, такой объект – «ничто». Он принадлежит классу NoneType . До этого мы знали четыре типа данных, они же четыре класса: int , float , str , bool . Пришло время пятого.

Когда после return ничего не указывается, то по умолчанию считается, что там стоит объект None . Но никто вам не мешает явно написать return None .

Более того. Ранее мы рассматривали функции, которые вроде бы не возвращали никакого значения, потому что в них не было оператора return . На самом деле возвращали, просто мы не обращали на него внимание, не присваивали никакой переменной и не выводили на экран. В Python всякая функция что-либо возвращает. Если в ней нет оператора return , то она возвращает None. То же самое, как если в ней имеется «пустой» return .

# Возврат нескольких значений

В Питоне позволительно возвращать из функции несколько объектов, перечислив их через запятую после команды return :

Из функции cylinder() возвращаются два значения. Первое из них присваивается переменной sCyl, второе – fCyl. Возможность такого группового присвоения – особенность Python, обычно не характерная для других языков:

Фокус здесь в том, что перечисление значений через запятую (например, 10, 15, 19) создает объект типа tuple. На русский переводится как «кортеж». Это разновидность структур данных, которые будут изучены позже.

Когда же кортеж присваивается сразу нескольким переменным, то происходит сопоставление его элементов соответствующим в очереди переменным. Это называется распаковкой.

Таким образом, когда из функции возвращается несколько значений, на самом деле из нее возвращается один объект класса tuple. Перед возвратом эти несколько значений упаковываются в кортеж. Если же после оператора return стоит только одна переменная или объект, то ее/его тип сохраняется как есть.

Распаковка не является обязательной. Будет работать и так:

На экран выводится кортеж, о чем говорят круглые скобки. Его также можно присвоить одной переменной, а потом вывести ее значение на экран.

# Упражнения

Напишите программу, в которой вызывается функция, запрашивающая с ввода две строки и возвращающая в программу результат их конкатенации. Выведите результат на экран.

Напишите функцию, которая считывает с клавиатуры числа и перемножает их до тех пор, пока не будет введен 0. Функция должна возвращать полученное произведение. Вызовите функцию и выведите на экран результат ее работы.

# Параметры и аргументы функции

В программировании функции могут не только возвращать данные, но также принимать их, что реализуется с помощью так называемых параметров, которые указываются в скобках в заголовке функции. Количество параметров может быть любым.

Параметры представляют собой локальные переменные, которым присваиваются значения в момент вызова функции. Конкретные значения, которые передаются в функцию при ее вызове, будем называть аргументами. Следует иметь в виду, что встречается иная терминология. Например, формальные параметры и фактические параметры. В Python же обычно все называют аргументами.

Рассмотрим схему и поясняющий ее пример:

Когда функция вызывается, то ей передаются аргументы. В примере указаны глобальные переменные num1 и num2. Однако на самом деле передаются не эти переменные, а их значения. В данном случае числа 100 и 12. Другими словами, мы могли бы писать mathem(100, 12). Разницы не было бы.

Когда интерпретатор переходит к функции, чтобы начать ее исполнение, он присваивает переменным-параметрам переданные в функцию значения-аргументы. В примере переменной a будет присвоено 100, b будет присвоено 12.

Изменение значений a и b в теле функции никак не скажется на значениях переменных num1 и num2. Они останутся прежними. В Python такое поведение характерно для неизменяемых типов данных, к которым относятся, например, числа и строки. Говорят, что в функцию данные передаются по значению. Так, когда a присваивалось число 100, то это было уже другое число, не то, на которое ссылается переменная num1. Число 100 было скопировано и помещено в отдельную ячейку памяти для переменной a.

Существуют изменяемые типы данных. Для Питона, это, например, списки и словари. В этом случае данные передаются по ссылке. В функцию передается ссылка на них, а не сами данные. И эта ссылка связывается с локальной переменной. Изменения таких данных через локальную переменную обнаруживаются при обращении к ним через глобальную. Это есть следствие того, что несколько переменных ссылаются на одни и те же данные, на одну и ту же область памяти.

Необходимость передачи по ссылке связана в первую очередь с экономией памяти. Сложные типы данных, по сути представляющие собой структуры данных, обычно копировать не целесообразно. Однако, если надо, всегда можно сделать это принудительно.

# Произвольное количество аргументов

Обратим внимание еще на один момент. Количество аргументов и параметров совпадает. Нельзя передать три аргумента, если функция принимает только два. Нельзя передать один аргумент, если функция требует два обязательных. В рассмотренном примере они обязательные.

Однако в Python у функций бывают параметры, которым уже присвоено значение по-умолчанию. В таком случае, при вызове можно не передавать соответствующие этим параметрам аргументы. Хотя можно и передать. Тогда значение по умолчанию заменится на переданное:

При втором вызове cylinder() мы указываем только один аргумент. Он будет присвоен переменной-параметру h. Переменная r будет равна 1.

Согласно правилам синтаксиса Python при определении функции параметры, которым присваивается значение по-умолчанию должны следовать (находиться сзади) за параметрами, не имеющими значений по умолчанию.

А вот при вызове функции, можно явно указывать, какое значение соответствует какому параметру. В этом случае их порядок не играет роли:

… figure3 = cylinder(10, 2) figure4 = cylinder(r=2, h=10) print(figure3) print(figure4) В данном случае оба вызова – это вызовы с одними и теми же аргументами-значениями. Просто в первом случае сопоставление параметрам-переменным идет в порядке следования. Во-втором случае – по ключам, которыми выступают имена параметров.

В Python определения и вызовы функций имеют и другие нюансы, рассмотрение которых мы пока опустим, так как они требуют более глубоких знаний, чем у нас есть на данный момент. Скажем лишь, что функции может быть определена так, что в нее можно передать хоть ни одного аргумента, хоть множество:

Опять же, судя по скобкам, здесь возникает упомянутый в прошлом уроке кортеж.

# Упражнения

Напишите программу, которая:

  • просит ввести число,
  • проверяет возможность преобразовать введенные данные в число,
  • преобразовывает значение в число и выводит число в консоль.

В программе должны быть определены следующие четыре функции:

  • Функция get_input() не имеет параметров, запрашивает ввод с клавиатуры и возвращает в основную программу полученную строку.
  • Функция test_input() имеет один параметр. В теле она проверяет, можно ли переданное ей значение преобразовать к целому числу. Если можно, возвращает логическое True . Если нельзя – False .
  • Функция str_to_int() имеет один параметр. В теле преобразовывает переданное значение к целочисленному типу. Возвращает полученное число.
  • Функция print_int() имеет один параметр. Она выводит переданное значение на экран и ничего не возвращает.

В основной ветке программы вызовите первую функцию. То, что она вернула, передайте во вторую функцию. Если вторая функция вернула True , то те же данные (из первой функции) передайте в третью функцию, а возвращенное третьей функцией значение – в четвертую.

Напишите преобразователь римских цифр (I, II, . L) в арабские (1, 2, . 50):

  • в консоли просят ввести римское число;
  • функция is_rim_number(num) принимает на вход число, проверяет является ли введенное значение римской цифрой и возвращает True — если является или False — если нет;
  • функция rim_to_int(num) принимает на вход число, преобразует римскую цифру в целое число и возвращает полученное число.
  • вывести ответ в консоль.

Напишите программу преобразования арабских целых чисел от 1 до 900 в римские.

Напишите программу преобразования римских цифр от 1 до 900 в арабские.

Напишите программу калькулятор для римских цифр, с операциями сложения и вычитания. Программа должна уметь работать с цифрами от I до L:

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *