Привет, Хабр!
Область видимости — это часть программы, в которой можно обращаться к определённой переменной или функции. В Python области видимости определяют, где и как можно использовать переменные.
Понимание областей видимости помогает избежать очень серьезных проблем. Например, случайное переопределение глобальных переменных внутри функций или использование переменных до их объявления могут привести к трудноуловимым багам.
Для определения последовательности, в которой Python ищет значение переменной, используется правило LEGB. Это правило представляет собой порядок поиска переменных в следующих областях видимости: Local (локальная), Enclosing (вложенная), Global (глобальная) и Built-in (встроенная).
Сразу про встроенную область
Встроенная область видимости в Python содержит предопределенные функции, типы и исключения, которые доступны в любой части программы. Эта область видимости загружается автоматом при запуске интерпретатора Python и доступна в любом месте программы.
Python предоставляет множество встроенных функций и атрибутов. Некоторые из них:
print()- используется для вывода данных на экран.len()- возвращает длину объекта (списка, строки и т.д.).type()- возвращает тип объекта.input()- считывает строку ввода от пользователя.int(),float(),str()- функции для преобразования типов данных.range()- генерирует последовательность чисел.sum()- возвращает сумму элементов последовательности.min(),max()- возвращают минимальный и максимальный элементы в последовательности.sorted()- возвращает отсортированный список.
Локальная область видимости
Локальная область видимости касается переменных, которые объявлены внутри функции или лямбда-выражения. Эти переменные доступны только в пределах функции, в которой они объявлены, и уничтожаются после завершения выполнения этой функции.
Локальные переменные объявляются внутри функции и доступны только в рамках этой функции. Их область видимости ограничена телом функции, и они недоступны за ее пределами.
def greet():
message = "Hello, World!" # kокальная переменная
print(message)
greet() # выведет: Hello, World!
print(message) # ошибка: NameError, так как 'message' недоступна за пределами функции
Лямбда-выражения также могут содержать локальные переменные, которые существуют только в контексте выполнения этой лямбды.
square = lambda x: x ** 2
print(square(5)) # выведет: 25
Локальные переменные создаются при каждом вызове функции и уничтожаются после завершения ее выполнения. Т.е при каждом вызове функции создаются новые экземпляры локальных переменных, независимо от предыдущих вызовов.
def counter():
count = 0 # локальная переменная
count += 1
print(count)
counter() # выведет: 1
counter() # выведет: 1 (новая переменная 'count' создается при каждом вызове функции)
Примеры применения
Локальные переменные отлично подходят для временного хранения промежуточных результатов, необходимых только в пределах одной функции:
def process_data(data):
temp_result = [] # локальная переменная для временного хранения
for item in data:
temp_result.append(item * 2)
return temp_result
data = [10, 20, 30]
print(process_data(data)) # выведет: [20, 40, 60]
Лямбда-выражения часто используются в качестве аргументов функций, таких как map, filter и reduce:
numbers = [1, 2, 3, 4, 5]
squared = list(map(lambda x: x ** 2, numbers))
print(squared) # выведет: [1, 4, 9, 16, 25]
Закрытия позволяют внутренним функциям запоминать состояние их окружения:
def outer_function():
message = "Hello"
def inner_function():
print(message)
return inner_function
my_function = outer_function()
my_function() # выведет: Hello
Локальные переменные могут использоваться для управления состоянием внутри функции:
def search_item(data, target):
found = False # локальная переменная для отслеживания состояния
for item in data:
if item == target:
found = True
break
return found
data = [1, 2, 3, 4, 5]
print(search_item(data, 3)) # выведет: True
print(search_item(data, 6)) # выведет: False
Глобальная область видимости
Глобальная область видимости в Python охватывает все переменные, определенные на самом верхнем уровне скрипта или модуля.
Глобальные переменные объявляются вне всех функций и доступны для использования в любом месте программы. Их жизненный цикл продолжается на протяжении всего выполнения программы. Например:
global_var = "I am a global variable"
def print_global():
print(global_var)
print_global() # выведет: I am a global variable
Для чтения глобальных переменных внутри функции никаких специальных действий не требуется. Однако для их изменения необходимо использовать ключевое слово global. Без этого ключевого слова Python создаст новую локальную переменную с тем же именем, что и глобальная, но не изменит глобальную переменную.
Чтение глобальной переменной внутри функции:
count = 10
def print_count():
print(count) # использует глобальную переменную count
print_count() # выведет: 10
Модификация глобальной переменной внутри функции:
count = 10
def increment_count():
global count
count += 1
increment_count()
print(count) # выведет: 11
Примеры использования
Использование глобальных переменных для хранения конфигурации:
config = {
"host": "localhost",
"port": 8080
}
def print_config():
print(f"Host: {config['host']}, Port: {config['port']}")
print_config()
# Выведет: Host: localhost, Port: 8080
Управление состоянием через глобальные переменные:
is_logged_in = False
def log_in():
global is_logged_in
is_logged_in = True
def log_out():
global is_logged_in
is_logged_in = False
def check_login_status():
return "Logged in" if is_logged_in else "Logged out"
print(check_login_status()) # Выведет: Logged out
log_in()
print(check_login_status()) # Выведет: Logged in
Глобальные переменные и многопоточность:
import threading
counter = 0
lock = threading.Lock()
def increment():
global counter
with lock:
for _ in range(10000):
counter += 1
threads = [threading.Thread(target=increment) for _ in range(10)]
for thread in threads:
thread.start()
for thread in threads:
thread.join()
print(counter) # ожидаемый результат: 100000
Область видимости в пределах функции
Вложенные функции — это функции, определенные внутри других функций. Вложенные функции могут обращаться к переменным, определенным в их внешних функциях. Эта способность делает возможным создание замыканий, где внутренняя функция запоминает контекст, в котором она была создана, даже после завершения выполнения внешней функции.
nonlocal — это ключевое слово, используемое для объявления переменной, находящейся во внешней функции, но не в глобальной области видимости. Оно позволяет внутренней функции изменять значение этой переменной.
def outer_function():
message = "Hello, World!"
def inner_function():
nonlocal message
message = "Hello, Python!"
inner_function()
print(message)
outer_function() # выведет: Hello, Python!
nonlocal используется для указания, что переменная, находящаяся в локальной области видимости внутренней функции, должна быть связана с переменной, определенной во внешней функции. Так можно изменять переменные, которые находятся в области видимости между локальной и глобальной.
def outer_function():
outer_var = "outer"
def middle_function():
middle_var = "middle"
def inner_function():
nonlocal outer_var, middle_var
outer_var = "modified outer"
middle_var = "modified middle"
inner_function()
print(middle_var)
middle_function()
print(outer_var)
outer_function()
# выведет:
# modified middle
# modified outer
Примеры
Фабричная функция возвращает вложенную функцию, которая использует переменные из внешней функции для выполнения своих задач:
def make_multiplier(factor):
def multiplier(x):
return x * factor
return multiplier
double = make_multiplier(2)
triple = make_multiplier(3)
print(double(5)) # выведет: 10
print(triple(5)) # выведет: 15
Счетчик вызовов функции, который отслеживает количество вызовов функции:
def make_counter():
count = 0
def counter():
nonlocal count
count += 1
return count
return counter
counter = make_counter()
print(counter()) # выведет: 1
print(counter()) # выведет: 2
print(counter()) # выведет: 3
Декораторы часто используют вложенные функции для изменения поведения других функций:
def my_decorator(func):
def wrapper():
print("Что-то происходит перед функцией.")
func()
print("Что-то происходит после функции.")
return wrapper
@my_decorator
def say_hello():
print("Hello!")
say_hello()
# Выведет:
# Что-то происходит перед функцией.
# Hello!
# Что-то происходит после функции.
Области видимости и встроенных функций позволяют создавать более структурированные программы без багов.
Больше про языки программирования и их особенности эксперты OTUS рассказывают в рамках практических онлайн-курсов. С полным каталогом курсов можно ознакомиться по ссылке.
Комментарии (5)

onegreyonewhite
03.07.2024 17:29+4Т.е. тут реально кто-то обсуждает, что в блоге OTUS'а "замыкания" перевели как "закрытия", но никто не оценил совершенство очепятки
kокальная переменная, которая настолько идеально передаёт качество самого материала?
Andrey_Solomatin
03.07.2024 17:29+1Опечатки это из другой оперы. Это случается с лучшими из нас. И не всегода вычитка все из ловит.
А вот использование неправильного термина, это уже показатель урованя знаний.
В статье хотя бы для чтения переменных из внешней области видимости не используется global. Я за 30 секнуд нашёл у нас в компании пару примеров такого кода.
kosdmit
Все таки в русском языке принят термин "Замыкания" для этого понятия. Это перевод статьи?
NN1
Видимо close - закрыть, closure - закрытие .
Впервые такое встречаю.
kosdmit
Верно. В зарубежной литературе для функций захватывающих внешнюю область видимости используют термин "Closures", вот только переводить его принято как "Замыкания". Термин "Закрытие" обычно не используется в данном контексте.