Кратко о локальных и глобальных переменных
Подробнее о namespace читайте в следующем разделе. Здесь будет короткое описание основных моментов.
Область видимости (пространство имен) - область, где хранятся переменные. Здесь определяются переменные и делают поиск имен.
Операция = связывает имена с областью видимости (пространством имен)
Пока мы не написали ни одной функции, все переменные в программе глобальные.
Глобальные переменные видны во всех функциях программы. Они должны быть сначала созданы, а потом их можно читать и менять.
Создаются переменные присвоением =
Обычно пишут программу так:
a = 1 # создали раньше, чем ее использовали
def f():
print(a) # читаем глобальную переменную а (не изменяя ее значения)
f() # тут (дальше) мы использовали переменную а
Этот код будет работать так же:
def f():
print(a) # глобальная переменная а
a = 1 # создали раньше, чем ее использовали
f() # тут (дальше) мы использовали переменную а
Напечатает 1. Глобальная переменная а сначала была создана, а потом была вызвана функция f(). В функции f() видна глобальная переменная a.
Особенность интерптерируемого языка. Сначала - это раньше в процессе выполнения, а не "на строке с меньшим номером".
Локальная переменная создается внутри функции или блока (например, if или while). Локальная переменная видна только внутри того блока (функции), где была создана.
def f():
a = 1 # локальная переменная функции f
f()
print(a) # ошибка, локальная переменная а не видна вне функции f.
Ошибка "builtins.NameError: name 'a' is not defined"
- Локальные переменные создаются =.
- Каждый вызов функции создает локальную переменную (свою, новую) (каждый вызов функции создает свой новый namespace)
- после завершения функции ее локальные переменные уничтожаются.
- аргументы функции тоже являются локальными переменными (при вызове функции идет = параметру значения).
Итого: Если в функции было =, то мы создали локальную переменную. Если = не было, то читаем глобальную переменную.
Можно создавать в разных функциях локальные переменные с одинаковыми именами. В функциях foo и bar создали переменные с одинаковыми именами а.
Можно (но не надо так делать!) создавать локальную переменную с тем же именем, что и глобальную. pylint
поможет найти такие переменные.
def f():
a = 1 # создана локальная переменная а=1
print(a, end=' ') # печатаем локальную переменную а=1
a = 0 # создана глобальная переменная а=0
f()
print(a) # печатаем глобальную переменную а=0
Напечатает 1 0
.
- создается глобальная переменная а = 0
- вызывается f()
- в f создается локальная переменная а = 1 (теперь нельзя доступиться из функции f к глобальной переменной a)
- в f печатается локальная переменная a = 1
- завершается f
- печатается глобальная переменная а = 0
Переменная в функции будет считаться локальной, если она будет создана внутри условного оператора, который никогда не выполнится:
def f():
print(a) # UnboundLocalError: local variable 'a' referenced before assignment
if False:
a = 0 # тут создаем локальную переменную а внутри функции f
a = 1 # глобальная переменная а
f()
global говорит, что переменная относится к глобальному namespace. (В этот момент переменная НЕ создается). Переменную можно создать позже.
def f():
global a
a = 1
print(a, end=' ')
a = 0
f()
print(a)
выведет "1 1", т.к. значение глобальной переменной будет изменено внутри функции.
Рекурсивный вызов функции
Так как каждый вызов функции создает свое собственное пространство имен, можно писать функции рекурсивно.
Например, n! = n * (n-1)!, 0! = 1. Запишем это математическое определение факториала в виде кода.
def fact(n):
if n == 0:
return 1
return n * fact(n-1)
print(fact(5))
При вызове fact(5) создается namespace c n=5, далее идет вызов f(4) и создается еще один namespace, в нем n=4 (это другая переменная n, она в другом пространстве имен и та n=5 из этого пространства не доступна).
Вложенные области видимости
Можно определять одну функцию внутри другой.
Чтение переменной внутри функции. Ищем имя:
- в локальной области видимости функции;
- в локальных областях видимости объемлющих функций изнутри наружу;
- в глобальной области видимости модуля;
- в builtins (встроенная область видимости).
x = value
внутри функции:
- создает или изменяет имя х в текущей локальной области видимости функции;
- если был
unlocal x
, то = создает или изменяет имя в ближайшей области видимости объемлющей функции. - если был
global x
, то = создает или изменяет имя в области видимости объемлющего модуля.
X = 99 # Имя в глобальной области видимости: не используется
def f1():
X = 88 # Локальное имя в объемлющей функции
def f2():
print(X) # Обращение к переменной во вложенной функции
f2()
f1() # Выведет 88: локальная переменная в объемлющей функции
f2() # Ошибка! функция f2 здесь не видна!
В f2() нельзя изменить значение Х, принадлежащей функции f1(). Вместо этого будет создана еще одна локальная переменная, но уже в пространстве имен функции f2().
Напечатает 77 88
:
X = 99 # Имя в глобальной области видимости: не используется
def f1():
X = 88 # Локальное имя в объемлющей функции
def f2():
X = 77 # создаем локальную переменную
print(X) # 77 - обращение к локальной переменной функции f2()
f2()
print(X) # 88 - обращение к локальной переменной функции f1()
f1()
Если нужно изменять значение переменной Х, которая принадлежит пространству имен объемлющей (enclosed) функции, то добавляют unlocal
X = 99 # Имя в глобальной области видимости: не используется
def f1():
X = 88 # Локальное имя в функции f1
def f2():
unlocal X # X принадлежит объемлющей функции
X = 77 # изменяем переменную функции f1
print(X) # 77 - обращение к локальной переменной объемлющей функции f1()
f2()
print(X) # 77 - обращение к локальной переменной функции f1()
f1()
Правило LEGB
При определении, к какому namespace относится имя, используют правило LEGB:
- Когда внутри функции выполняется обращение к неизвестному имени, интерпретатор пытается отыскать его в четырех областях видимости – в локальной (local, L), затем в локальной области любой объемлющей инструк-
ции def (enclosing, E) или в выражении lambda, затем в глобальной (global, G) и, наконец, во встроенной (built-in, B).
- Поиск завершается, как только будет найдено первое подходящее имя.
- Если требуемое имя не будет найдено, интерпретатор выведет сообщение об ошибке.