Локальное хранилище потоков в Python (threading.local)
@python_quizРазберем этот квиз
Короткий заголовок: Поведение threading.local при доступе из разных потоков
Условие — код из квиза

Нужно понять, что напечатает этот фрагмент и почему.
Что делает код — пошагово
- Создаётся объект local() — это специальный контейнер, который предоставляет отдельные наборы атрибутов для каждого потока.
- В главном (основном) потоке задаётся
storage.x = 0. - Создаётся и запускается новый поток, в котором в целевой функции читается
storage.xчерезgetattr(storage, 'x', 'no x'). Если атрибутxотсутствует в контексте этого потока, возвращается строка'no x'. - После завершения потока (join) главный поток печатает
storage.x.
Как работает threading.local
- Объект local хранит значения атрибутов отдельно для каждого потока. Атрибут, установленный в одном потоке, не виден в другом.
- Поэтому установка
storage.x = 0в главном потоке не автоматически создаётxв новом потоке.
Ожидаемый вывод
При выполнении кода будет напечатано сначала то, что вернул getattr в потоке, а затем значение в главном потоке. Конкретно:

Первой строкой выводится 'no x' (поток не видит storage.x), затем 0 (значение, установленное в главном потоке).
Разбор вариантов ответов (почему некоторые варианты неверны)
- "Сначала 0, потом 0" — неверно. Это предполагало бы, что атрибуты local разделяются *не* по потокам, и новое значение видимо в дочернем потоке. На самом деле дочерний поток не получает атрибуты из главного.
- "Оба раза будет 'no x'" — неверно. Главный поток явно установил
storage.x = 0, поэтому при печати в главном потоке будет 0. - "Сначала 'no x', потом 0" — соответствует поведению threading.local: дочерний поток не видит
x, а главный поток печатает своё значение. - "Сначала 0, потом 'no x'" — неверно: сначала дочерний поток не видит
x, поэтому он не сможет напечатать 0.
Дополнительный пример — показать изоляцию для двух потоков
Ниже пример, где каждый поток устанавливает своё значение x, чтобы увидеть, что они не мешают друг другу:

Ожидаемый вывод (порядок печати потоков может меняться):

Здесь видно: каждый поток имеет своё поле x, а главный поток не видит их значения.
Вывод
threading.local обеспечивает изолированные наборы атрибутов для каждого потока. Если установить атрибут в одном потоке, другие потоки не увидят это значение, пока сами не установят свой собственный атрибут. В рассматриваемом фрагменте дочерний поток не найдёт x и вернёт дефолт 'no x', а главный поток напечатает своё значение 0.