Локальные и глобальные переменные в Python
При программировании на Python, как и во всяком другом языке всегда крайне важно четко понимать сущность, отличия, нюансы применения и возможные последствия от использования переменных различных областей видимости. Ведь, по большому счету, от такого четкого понимания роли различных типов переменных в конечном счете зависит структура и логика наших программ в целом, то какие пользовательские классы, объекты и модули мы в этих программах сформируем.
В Python для переменных существует 4-ре следующие области видимости: локальная, нелокальная, глобальная и системная.
Системная область видимости шефствует над системными переменными из операционок ваших вычислительных средств и является наиболее универсальной, поскольку обеспечивает доступ к этим переменным из любого модуля или функции в ваших программах. В тоже время, переменные из данной области видимости относительно не часто используются в кодинге и, в основном применяются при разработке системных утилит или программ тесно связанных с возможностями операционных систем, в которых они выполняются.
Еще одной довольно специфической в применении является нелокальная область видимости, переменные которой используются главным образом во вложенных функциях тогда, когда необходимо указать, что для конкретной вложенной функции определенная переменная не является ни локальной, ни глобальной с точки зрения всей программы (модуля).
Таким образом, априори можно констатировать, что наиболее распространенными являются локальные и глобальные переменные, о нюансах применения которых пойдет речь далее в этой статье.
Определение локальных и глобальных переменных
Глобальной переменной называется та переменная, доступ к которой можно получить из любого места в коде вашего модуля – отдельного программного файла. Объявление этих переменных производится вне любых программных блоков типа функций, классов или процедур и, обычно помещается в начало файла с программным кодом.
Важно отметить, что программа на Python может состоят из нескольких отдельных программных файлов (нескольких модулей), но переменная будет являться глобальной лишь в том модули, в котором она была объявлена. Из других модулей, независимо от того являются ли они частями одной программы или нет, данная переменная видна не будет.
Локальной переменной может быть любая переменная, объявленная внутри какого-либо программного блока типа функции, класса, метода или процедуры. Основным отличием локальных переменных от глобальных является то, доступ к первым может осуществляться лишь в том программном блоке, в котором они были объявлены.
Чтение глобальных переменных
Давайте в самом начале нашей программы объявим переменную privetstviye
, которую инициализируем строкой “Привет земляне!”:
>>> privetstviye = "Привет земляне!"
Затем, давайте объявим простенькую функцию prosmotr_privetstviya()
, которая будет выводить на печать нашу раннее объявленную переменную privetstviye
:
>>> def prosmotr_privetstviya():
... print(privetstviye)
...
Как и следовало ожидать, в результате вызова вышеназванной функции мы увидим выведенную на дисплей строку “Привет земляне!”:
>>> prosmotr_privetstviya()
Привет земляне!
Хотя переменная privetstviye
в функции prosmotr_privetstviya()
и не была объявлена, данная функция все-таки без проблем смогла ее вывести внутри себя. Это объясняется тем, что данную переменную мы объявили в глобальной области видимости - в начале программы и вне всяких программных блоков. Такое место объявления нашей переменной автоматически придало ей статус глобальной – видимой во всех программных блоках, имеющихся в данном модули (программном файле).
Изменение значений у локальных и глобальных переменных
Как мы уже знаем, всегда при объявлении переменных внутри каких-либо программных блоков, Python присваивает этим переменным статус локальных, то есть тех, которые являются доступными лишь внутри блока, где они были объявлены. Кроме того, те переменные, которые передаются пользовательским функциям в качестве аргументов, для самых этих функций также являются локальными.
На примерах раннее мы уже выяснили, что внутри программных блоков (функций) запросто можно использовать глобальные переменные для вывода их значений. Теперь же давайте выясним, можно ли внутри таких функций изменять значения глобальным переменным. Для этого объявим дополнительную пользовательскую функцию izmenyayemoye_privetstviya()
, которая принимая в качестве своего аргумента utochneniye
уточнение для приветствия, будет конкатенировать (объединять) его вместе со словом “Привет” и присваивать полученное значение нашей “якобы” глобальной переменной privetstviye из предыдущего раздела нашей статьи. В дополнение к этому вышеназванная функция буде еще и выводить получившиеся значение из переменной privetstviye
на дисплей:
>>> def izmenyayemoye_privetstviya(utochneniye):
... privetstviye = f"Привет {utochneniye}!"
... print(privetstviye)
...
Как вы думаете, что нам выведет функция izmenyayemoye_privetstviya()
с переданным, например, аргументом “инопланетяне”, а что покажет непосредственно сама переменная privetstviye
, выведенная через стандартную функцию print(). Давайте для наглядности приведем полный листинг кода с этим примером:
privetstviye = "Привет земляне!"
def izmenyayemoye_privetstviya(utochneniye):
privetstviye = f"Привет {utochneniye}!"
print(privetstviye)
izmenyayemoye_privetstviya("инопланетяне")
print(privetstviye)
# Привет инопланетяне!
# Привет земляне!
Из приведенного выше примера четко видно, что в программах на Python вполне себе могут уживаться глобальные и локальные переменные имеющие одинаковые имена. Но, вот как раз по этой причине, Python не позволяет поменять значения у глобальных переменных внутри каких-либо программных блоков. Как только мы попытаемся это сделать внутри одного из таких блоков, наш любимый интерпретатор именно в этом блоке объявить дополнительную переменную с тем же именем, но не с глобальной, а с локальной областью видимости.
Затенение переменных
Итак, мы уже выяснили, что внутри каких-либо программных блоков значения, как глобальных, так и локальных переменных являются равнодоступными только для чтения. При этом, если у нас глобальная и локальная переменные имеют одинаковые имена и, мы попытаемся одну из этих переменных прочитать внутри какой-либо функции, класса или любого другого программного блока, то приоритет всегда будет отдан именно локальной переменной.
Если же внутри программного блока нам нужно не прочитать, а изменить значение какой-либо переменной, то даже когда данная переменная является у нас глобальной, мы фактически способны изменить (инициализировать) исключительно только локальную переменную.
Иногда в Python специально для оптимизации кода используют вышеописанные особенности сочетания одноименных глобальных и локальных переменных, а сам процесс создания этих переменных с одинаковыми именами называют затенением. Вмести с тем, следует отметить, что многие опытные разработчики все-таки не склонны применять практику затенения переменных поскольку видят в ней негативное влияние на читабельность и логику восприятия разрабатываемого ими кода.
Совместное использование локальных и глобальных переменных
Интуитивно, на уровне подсознание, мы где-то уже почти осознали то, что внутри любого программного блока априори не может быть двух переменных с одинаковыми именами, одна из которых трактовалась бы интерпретатором, как глобальная, а другая, как локальная переменная. Но, давайте посмотрим, как будет реагировать наш интерпретатор в случае реального наличия такой ситуации. Для этого немного изменим раннее объявленную нами функцию izmenyayemoye_privetstviya()
таким образом, чтобы прежде она выводила бы на дисплей строку приветствия из нашей глобальной переменной privetstviye
, а затем переменной с тем же именем присваивала бы другую строку приветствие сформированную с учетом значения, полученного из своего аргумента. В конце же данной функции мы снова попытаемся вывести уже измененное содержимое переменной privetstviye
на дисплей.
>>> def izmenyayemoye_privetstviya(utochneniye):
... print(privetstviye)
... privetstviye = f"Привет {utochneniye}!"
... print(privetstviye)
...
По идее можно было бы предположить, что в результате вызова только что откорректированной нами функции с аргументом:
izmenyayemoye_privetstviya("инопланетяне")
у нас на дисплее сначала выведется содержание нашей предварительно инициализированной глобальной переменной (Привет земляне!), а затем содержание нашей локальной переменной (Привет инопланетяне!), которая была инициализирована прямо внутри нашей функции. Но, на самом деле произойдет нечто иное:
privetstviye = "Привет земляне!"
def izmenyayemoye_privetstviya(utochneniye):
print(privetstviye)
privetstviye = f"Привет {utochneniye}!"
print(privetstviye)
izmenyayemoye_privetstviya("инопланетяне")
# Traceback (most recent call last):
# File "<stdin>", line 8, in <module>
# File "<stdin>", line 4, in izmenyayemoye_privetstviya
# UnboundLocalError: cannot access local variable 'privetstviye' where it is not associated with a value
Фактически, при вызове первого print(privetstviye)
в функции izmenyayemoye_privetstviya()
мы в нашем примере получили исключение UnboundLocalError, гласящее о том, что локальная переменная privetstviye не может быть выведена на дисплей, так как она еще не инициализирована.
На первый взгляд данная ошибка может показаться несуразностью, ведь мы предварительно объявили и инициализировали privetstviye вне всяких программных блоков, как глобальную переменную. А тут, в ошибке выдается, что она якобы превратилась в локальную, да еще не инициализированную переменную? Но, на самом деле это объясняется тем, что, анализируя код нашей функции, интерпретатор Python прежде всего просматривает операторы присваивания и, только потом отслеживает имена переменных, которым в нашем случае мы присвоили одинаковые имена privetstviye. Именно поэтому, видя в нашей функции оператор присвоения в отношении к переменной privetstviye, интерпретатор не только априори объявляет эту переменную локальной, но и любое другое упоминание ее имени в функции также воспринимает в качестве этой же локальной переменной.
Таким образом, в 4-й строке программы вместе со стандартной функцией print() у нас используется локальная переменная privetstviye, которая фактически инициализируется только на следующей, 5-й строке.
В общем случае необходимо отметить, что любое присвоение внутри программного блока делает из одноименной глобальной переменной, локальную. Следовательно, если эта переменная упоминается в программном блоке прежде, чем она была инициализирована, то это вызовет ошибку.
Выводы
Из данной статьи в отношении интерпретатора Python можно сделать следующие обобщающие выводы:
- Использование значений глобальных переменных допускается в любом месте модуля (файла с программой), как внутри программных блоков (функций, классов, методов или процедур), так и вне их.
- Значения локальных переменных внутри тех программных блоков, где они были объявлены могут без ограничений, как использоваться, так и изменяться.
- В случае наличия в модули одноименных глобальных и локальных переменных, внутри программных блоков могут быть доступны лишь значения объявленных там локальных переменных. Этот процесс называется затенением переменных.
- Внутри программных блоков каждая переменная должна однозначно восприниматься интерпретатором, либо как локальная, либо как глобальная. Двух одноименных переменных с разными областями видимости в интерпретаторе быть не может.
- Оператор присвоения внутри программных блоков в случае одноименных глобальных и локальных переменных всегда глобальную переменную замещает локальной, что может привести к ошибки при использовании данной переменной до ее инициализации.
Возможно будет интересно
🏆 Hello, world!
Мы вчера запустили новый www.pylot.me. Должны были в следующую среду, но запустили вчера.
Как практиковаться в Python?
Для улучшения качества знаний и повышения уровня программиста, необходим постоянный практикум. Где можно это организовать самостоятельно, и как практиковаться в Python?
Условные конструкции и сопоставление структурных шаблонов
Шпаргалка по условным конструкциям и сопоставлению структурных шаблонов