Вопрос с собеседования · Python

Как работают области видимости в Python?

JuniorФункции
Ещё спрашивают так:
  • Что такое правило LEGB?
  • Чем отличаются global и nonlocal?
  • Откуда берётся UnboundLocalError?
Коротко

Имя ищется по цепочке LEGB: Local → Enclosing → Global → Built-in.

  • Присваивание внутри функции делает имя локальным для всей функции
  • global — писать в переменную модуля, nonlocal — в переменную объемлющей функции

А ещё Циклы и if своей области не создают — только функции, классы, модули, comprehension

Подробно

Когда Python встречает имя, он ищет его последовательно:

  1. Local — локальные имена текущей функции;
  2. Enclosing — локальные имена объемлющих функций (для вложенных);
  3. Global — имена модуля;
  4. Built-in — встроенные: len, print, Exception.

Важно, когда решается «локальное ли имя»: при компиляции функции. Если в теле функции где-либо есть присваивание имени, оно локальное во всей функции. Поэтому код ниже падает с UnboundLocalError: x += 1 — это присваивание, значит x локальная, а читается она до того, как получила значение.

global x говорит, что x — имя модуля; nonlocal x — что это имя ближайшей объемлющей функции. Злоупотреблять global не стоит: состояние лучше передавать явно.

Блоки if, for, while, with новой области не создают: переменная цикла доступна после цикла. А генераторы списков и выражения-генераторы (comprehension) имеют свою область — их переменная наружу не утекает.

Пример

x = 10

def broken():
    x += 1          # UnboundLocalError

def fixed():
    global x
    x += 1

for i in range(3):
    pass
print(i)            # 2 — цикл не создаёт область

[j for j in range(3)]
# print(j)          # NameError — у comprehension своя область

Что хотят услышать

  • Порядок поиска LEGB.
  • Правило «присваивание делает имя локальным» и UnboundLocalError.
  • Разница между global и nonlocal.
  • Что блоки не создают области видимости.

Частые ошибки

  • Думать, что if или for создают свою область, как в C или Java.
  • Объяснять UnboundLocalError «порядком строк», не упоминая правило присваивания.

Проверьте себя

Один верный вариант. Подумайте, потом откройте ответ.

Показать варианты и ответ
  • ✗Имя ищется по цепочке Local → Global → Built-in, а nonlocal даёт запись в переменные модуля
  • ✗Цикл for создаёт свою область видимости
  • ✗Присваивание внутри функции меняет глобальную переменную без объявлений
  • ✓Поиск имени: Local → Enclosing → Global → Built-in; запись во внешние — через global и nonlocal

Что спросят дальше

Разбор темы

Связанные вопросы

Партнёр платформы

Разбор собеседования с sotto

После собеседования sotto разбирает разговор: оценка, каждый вопрос — хорошо, частично или слабо, чего не хватило в ответе и над чем поработать к следующему разу. Первые 30 минут бесплатно.

Разбор собеседования