Область действия функций Python
Когда вы используете имя в программе Python, например, имя переменной, имя функции и т.д., Python создает, изменяет или ищет это имя в пространстве имен. Пространство имен - это полный список имен, существующих в данном контексте.
Существует два типа пространств имен: глобальное пространство имен и локальное пространство имен.
Область видимости объекта определяет места в программе, где к нему можно получить доступ. По умолчанию объекты доступны только из того пространства имен, в котором они находятся. Объекты в глобальном пространстве имен доступны из любой точки программы, это имена, которые объявлены на верхнем уровне модуля или скрипта, то есть не внутри функции, класса и т. д. С другой стороны, имена, объявленные внутри блока, например, функции, являются локальными для этого блока и доступны только внутри него.
Когда мы определяем функцию, Python устанавливает для нее локальное пространство имен. Любой объект, объявленный внутри функции, будет доступен только в пределах этой функции, объект считается локальным для этой функции. Попытка получить доступ к локальным объектам вне их области видимости вызовет ошибку NameError
. Например:
def demo():
x = 100
print(x)
demo()
//100
print(x)
//NameError: name 'x' is not defined
Объекты внутри функции локализуются таким образом, чтобы они не сталкивались с объектами с похожими именами вне этой функции. Это позволяет использовать одно и то же имя для разных объектов в разных областях без возникновения конфликтов.
Если функция объявляет имя, которое также существует в глобальном пространстве имен, локальное имя имеет приоритет над глобальным и перезаписывает его только внутри этой функции. Пример:
x = 200
def demo():
x = 100
print(x)
demo()
//100
print(x)
//200
В приведенном выше примере есть две разные переменные с именем x
, одна - в глобальном пространстве имен со значением 200
, другая - в локальном пространстве имен функции demo
со значением 100
. Таким образом, значение x
определяется тем, где мы её используем. Внесение любых изменений в x
внутри функции demo
никак не влияет на значение x
вне её области видимости.
Разрешение имен, правило LEGB
Когда мы упоминаем объект по имени, интерпретатор Python использует подход «изнутри наружу», чтобы определить, к какому объекту мы обращаемся. Этот подход часто называют правилом LEGB, которое расшифровывается как Local, Enclosing, Global, Built-in. Вкратце это правило можно описать следующим образом:
- Local, первое: Ищите это имя сначала в локальном пространстве имен и используйте локальную версию, если она доступна. Если нет, перейдите к более высокому пространству имен.
- Enclosing, второе: Если текущая функция заключена в другую функцию, ищите это имя во внешней функции. Если нет, перейдите к более высокому пространству имен.
- Global, третье: Ищите имя в объектах, определенных в глобальном пространстве имен.
- Built-in, последнее: Наконец, найдите переменную среди встроенных имен Python.
Если интерпретатор проходит все 4 этапа и не находит имя, то возникает ошибка NameErroris
, конечно, если операция не была операцией присваивания, в этом случае объект с таким именем будет создан в локальной области видимости, а ошибка не возникнет.
Пример:
x = 200
def demo():
x = 300
def inner():
print(x)
inner()
demo()
//300
В приведенном выше случае при вызове print x
интерпретатору не удается найти объект с именем x
в области видимости функции inner
, он переходит к вложенной функции, в которой находит объект x
со значением 300
, прекращая тем самым поиск. Если бы объект с именем x
всё же не был найден в объемлющей функции demo
, то был бы возвращен глобальный x
со значением 200
.
Другие примеры:
x = 500
def demo():
print(x)
x = 300
print(x)
demo()
//500
//300
def demo2():
y = 400
def inner():
print(x + y)
inner()
demo2()
//900
Оператор global
Оператор global
- это единственное средство, способное преобразовать имя, определенное внутри функции, в глобальное имя. Это означает, что имя будет вести себя так же, как имена, определенные в глобальном пространстве имен, и будет доступно из любой точки программы.
Примеры:
def demo():
global x
x = 200
demo()
print(x)
//200
Если существует глобальное имя, аналогичное имени, указанному в операторе global
, то все изменения, сделанные с объектом, который он идентифицирует, будут действовать на глобальном уровне.
Примеры:
x = 100
def demo():
global x
x = 500
demo()
print(x)
//500
Чтобы объявить несколько глобальных имен в одном глобальном операторе, мы используем ключевое слово global
, за которым следуют имена, разделенные запятыми, например:
def demo():
global x, y, z
x = 4
y = 3
z = x + y
demo()
print(x, y, z)
//4, 3, 7
Хотя оператор global
иногда может быть полезен, он может сделать код непонятным, так как затрудняет отслеживание глобальных имен. Поэтому его следует избегать, когда это возможно, ведь все имена внутри функции по умолчанию делаются локальными для этой функции, потому что это лучшая практика. Вместо этого мы можем передавать глобальные значения в качестве аргументов функции, а затем возвращать измененные значения.
Оператор nonlocal
Оператор nonlocal
является близким родственником оператора global
. В то время как оператор global
делает имя доступным на глобальном уровне, ператор nonlocal
делает имя доступным в области видимости вложенной функции. Это также позволяет вложенной функции вносить изменения в переменные, определенные во вложенной функции. Примеры:
def demo():
x = 400
def inner():
nonlocal x
x = 600
inner()
print(x)
demo()
//600
def demo2():
def inner():
nonlocal y
y = 500
inner()
print(y)
demo2()
//500
Примечание: Мы не можем использовать оператор nonlocal
с функцией верхнего уровня, при попытке это сделать возникнет SyntaxError
.
def demo():
nonlocal y
y = 100
demo()
//SyntaxError: no binding for nonlocal 'y' found