Кратко о локальных и глобальных переменных

Подробнее о 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.

  1. создается глобальная переменная а = 0
  2. вызывается f()
  3. в f создается локальная переменная а = 1 (теперь нельзя доступиться из функции f к глобальной переменной a)
  4. в f печатается локальная переменная a = 1
  5. завершается f
  6. печатается глобальная переменная а = 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).
    • Поиск завершается, как только будет найдено первое подходящее имя.
    • Если требуемое имя не будет найдено, интерпретатор выведет сообщение об ошибке.

results matching ""

    No results matching ""