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

Допустим, в программе две функции, и в обеих внутри используется переменная result:

Python 3.13
def area_circle(radius):
    result = 3.14 * radius * radius
    return result

def area_square(side):
    result = side * side
    return result

Эти два result друг другу не мешают: каждая функция работает со своим именем, и одно никак не влияет на другое. Это называется область видимости (scope) — правило, по которому Python решает, какая переменная имеется в виду в каждом месте кода.

Что такое область видимости?

Область видимости (scope) переменной — это часть программы, в которой эта переменная доступна. Она определяет, где имя можно использовать и какому объекту оно соответствует.

В Python два главных уровня видимости:

  • глобальная область: всё, что объявлено в основном теле модуля; видно из любой функции
  • локальная область: переменные, объявленные внутри функции; живут только пока функция выполняется

Удобно представлять это как комнаты в квартире. Вещи на твоём личном столе доступны только тебе (это локальная переменная функции). А вот общее объявление на холодильнике видит вся семья (это глобальная переменная).

Глобальные и локальные переменные: основная программа и две функции

Глобальные переменные

Глобальные переменные определяются вне функций и доступны как изнутри функций, так и снаружи:

Python 3.13
# Создаём глобальную переменную
>>> message = "Привет, мир!"

>>> def show_message():
...     # Используем глобальную переменную внутри функции
...     print(message)

>>> show_message()
Привет, мир!
>>> print(f"Переменная вне функции: {message}")
Переменная вне функции: Привет, мир!

Чем больше глобалов, тем сложнее отлаживать код: чтобы понять, что делает функция, недостаточно смотреть на её аргументы — нужно ещё помнить, какие переменные снаружи она читает.

Локальные переменные

Локальные переменные создаются внутри функций и существуют только пока функция выполняется. После её завершения они исчезают из памяти:

Python 3.13
>>> def calculate_sum():
...     a = 10
...     b = 20
...     result = a + b
...     print(f"Сумма внутри функции: {result}")

>>> calculate_sum()
Сумма внутри функции: 30
# Попытка обратиться к локальной переменной вне функции >>> try: ... print(f"result: {result}") ... except NameError as e: ... print(f"Ошибка: {e}")
Ошибка: name 'result' is not defined

Переменная result существует только внутри calculate_sum() и наружу не выходит.

Когда имена совпадают

Если внутри функции создаётся переменная с тем же именем, что и глобальная, локальная имеет приоритет. Это называется shadowing: локальная «заслоняет» глобальную, не трогая её:

Python 3.13
>>> x = "глобальная"

>>> def test_scope():
...     x = "локальная"
...     print(f"Внутри функции: x = {x}")

>>> test_scope()
Внутри функции: x = локальная
>>> print(f"Вне функции: x = {x}")
Вне функции: x = глобальная

Это две разные переменные с одинаковым именем. Они как полные тёзки, живущие в разных городах: имя совпадает, но это разные люди.

Изменение глобальной переменной из функции

По умолчанию любое присваивание внутри функции Python считает созданием новой локальной переменной:

Python 3.13
>>> x = "глобальная"

>>> def modify_global():
...     x = "новая локальная"  # Это локальная, глобальная не меняется
...     print(f"Внутри функции: x = {x}")

>>> modify_global()
Внутри функции: x = новая локальная
>>> print(f"Вне функции: x = {x}")
Вне функции: x = глобальная

Чтобы функция реально изменила глобальную переменную, нужно явно сказать global x в начале функции:

Python 3.13
>>> x = "глобальная"

>>> def modify_global():
...     global x
...     x = "глобальная изменена"
...     print(f"Внутри функции: x = {x}")

>>> modify_global()
Внутри функции: x = глобальная изменена
>>> print(f"Вне функции: x = {x}")
Вне функции: x = глобальная изменена

Ключевое слово global сигналит читателю: «эта функция не просто работает со своими аргументами, она лезет наружу». Поэтому им стоит пользоваться, только когда другого пути нет.

Самая частая ловушка: UnboundLocalError

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

Python 3.13
x = 10

def show():
    print(x)        # читаем
    x = x + 1       # и тут же присваиваем

Кажется, что функция должна напечатать 10 и увеличить x до 11. Но запуск падает с ошибкой UnboundLocalError: local variable 'x' referenced before assignment.

В чём дело? Python смотрит на функцию целиком ещё до её выполнения и определяет, какие имена локальные. Видит x = ... внутри — значит, x объявлена в этой функции, она локальная. И в верхней строке print(x) Python пытается прочитать локальную x, которой ещё не присвоено значение, и падает.

Чтобы такая функция работала с глобальной x, нужен global x в начале функции. Но обычно лучше другой путь: не трогать глобал, а принять x параметром и вернуть новое значение:

Python 3.13
>>> def increment(value):
...     return value + 1

>>> x = 10
>>> x = increment(x)
>>> print(x)
11

Когда global нужен, а когда нет

Самый предсказуемый способ работать с данными — не модифицировать глобалы, а передавать значения через параметры и возвращать через return:

Python 3.13
>>> def add(a, b):
...     return a + b

>>> x = 5
>>> y = 10
>>> sum_result = add(x, y)
>>> print(f"{x} + {y} = {sum_result}")
5 + 10 = 15

Такая функция честная: её результат полностью определяется тем, что в неё пришло. Ничего «закулисного». Это упрощает и тестирование, и чтение кода — посмотрел на сигнатуру, и сразу всё понятно.

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

Проверка понимания

Давайте проверим, как вы поняли концепцию области видимости переменных.


В следующем уроке разберём базовые типы данных в Python: что прячется за int, str, float, bool, и какие операции с ними определены.