Модуль 6 · Функции и модули

Справочник по Python · модуль 6 · функции и модули

Глава 6.7

6.7

Игнорируемые значения: _

Подчёркивание — обычное имя переменной, которому по соглашению придан особый смысл: «это значение мне не нужно». Оно делает намерение видимым в местах, где часть данных приходится принять, но использовать не планируется.

Категория
Соглашения и распаковка
Уровень
Начальный
Связанные темы
Распаковка, циклы, включения
Зачем это знать
Показывать, какие значения не используются

§1Зачем помечать ненужное

Распаковка требует столько имён, сколько значений в коллекции. Даже если нужно одно, придумывать имена приходится всем.

main.pyимя y создано и не используется
data = (10, 20, 30)
x, y, z = data

print(x, z)

Читатель кода видит переменную y и невольно ищет, где она понадобится. Соглашение с подчёркиванием снимает этот вопрос сразу.

  1. Читаемость. Явно видно, что значение принято и намеренно отброшено.
  2. Меньше имён. Не приходится придумывать tmp, unused и подобные заглушки.
  3. Меньше ошибок. Ненужное значение не попадёт в расчёты по невнимательности.

§2_ — это обычное имя

Никакой магии в подчёркивании нет. Это допустимый идентификатор, и Python обращается с ним как с любой другой переменной.

main.pyзначение действительно сохраняется
_ = 5
print(_)
Выводобычная переменная
5

Особый смысл ему придают люди, а не интерпретатор. Отсюда практическое следствие: подчёркивание не экономит память и не отменяет присваивание — оно только сообщает о намерении.

Одно исключение

В интерактивной оболочке Python имя _ занято: туда автоматически попадает результат последнего выражения. В обычном скрипте этого не происходит.

§3Распаковка кортежа

Самое частое применение — пропустить позицию при распаковке.

main.pyвторое значение не нужно
data = (10, 20, 30)
x, _, z = data

print(x, z)
Выводпервое и третье значения
10 30

Количество имён слева по-прежнему должно совпадать с количеством значений справа: подчёркивание занимает позицию, а не пропускает её.

§4Вложенная распаковка

Структуру слева можно повторить как угодно глубоко — подчёркивание работает на любом уровне.

main.pyиз каждой пары нужен только второй элемент
data = ((1, 2), (3, 4), (5, 6))
(_, y1), (_, y2), (_, y3) = data

print(y1, y2, y3)
Выводвторые элементы пар
2 4 6
main.pyдата и время: нужны только день и час
datetime = ((5, 19, 2008), (10, 30, "am"))
(_, day, _), (hour, _, _) = datetime

print(day, hour)
Выводдва значения из шести
19 10

§5Повторное использование _

Предыдущий пример показывает важную деталь: подчёркивание встречается в одном присваивании пять раз, и это не ошибка.

Схемачто происходит на самом деле
(_, day, _), (hour, _, _) = datetime

_ последовательно получает 5, 2008, 30, "am"
и в итоге хранит последнее из них

Обычная переменная просто перезаписывается на каждом шаге. Именно поэтому подчёркивание годится только для того, что действительно не понадобится: после распаковки в нём осталось лишь последнее отброшенное значение.

§6Цикл без переменной

Когда цикл нужен ради количества повторов, а номер шага не используется, переменную цикла тоже помечают подчёркиванием.

main.pyповторить три раза
for _ in range(3):
    print("привет")
Выводтри одинаковые строки
привет
привет
привет

Это сигнал читателю: тело цикла не зависит от номера итерации. Если бы вместо _ стояло i, пришлось бы просматривать тело, чтобы в этом убедиться.

§7Часть пары в цикле

Функции вроде enumerate() выдают пару значений. Если нужна только одна половина, вторую отбрасывают.

main.pyиндекс не нужен
colors = ["red", "green", "blue"]

for _, color in enumerate(colors):
    print(color)
Выводтолько значения
red
green
blue
Здесь _ — признак лишнего кода

Если от enumerate() нужен только элемент, сам enumerate() не нужен: достаточно for color in colors. Подчёркивание уместно там, где пару выдаёт что-то неустранимое — например, dict.items() или готовая функция.

§8Ключи и значения словаря

Метод items() всегда возвращает обе части пары, и вот здесь отбросить ненужную — нормальный приём.

main.pyсначала без ключей, затем без значений
data = {"a": 1, "b": 2, "c": 3}

for _, value in data.items():
    print(value, end=" ")

print()

for key, _ in data.items():
    print(key, end=" ")
Выводзначения, затем ключи
1 2 3
a b c 

У словаря для этих двух случаев есть и прямые методы — data.values() и data.keys(). Форма с _ нужна, когда вместе с одной половиной пары используется что-то ещё, а вторая всё равно не пригодилась.

§9Результат функции

Функция может возвращать несколько значений сразу. Принять придётся все, использовать — не обязательно.

main.pyвозраст не нужен
def get_user():
    return "Аня", 25, "Москва"

name, _, city = get_user()

print(name, city)
Выводдва значения из трёх
Аня Москва

Функция возвращает кортеж, и его распаковка подчиняется тем же правилам, что и в §3.

§10Включения

Во включениях подчёркивание используют в обеих ролях — и как отброшенную часть пары, и как переменную-счётчик.

main.pyиз пар нужны только названия
data = [(1, "apple"), (2, "banana"), (3, "cherry")]
fruit_names = [fruit for _, fruit in data]

print(fruit_names)
Выводномера отброшены
['apple', 'banana', 'cherry']
main.pyпять нулей
zeros = [0 for _ in range(5)]
print(zeros)
Выводзначение не зависит от шага
[0, 0, 0, 0, 0]

Во втором примере _ подчёркивает главное: элемент один и тот же на каждом повторе, а range() задаёт только их количество.

§11Звёздочка и остаток

Подчёркивание сочетается со звёздочкой из главы 6.6. Запись *_ отбрасывает не одно значение, а сразу весь остаток.

main.pyнужен только край списка
numbers = [1, 2, 3, 4]

first, *_ = numbers
*_, last = numbers

print(first)
print(last)
Выводпервый и последний
1
4
ЗаписьЧто отбрасывает
x, _, z = dataровно одно значение в этой позиции
first, *_ = dataвсе значения после первого
*_, last = dataвсе значения до последнего
first, *_, last = dataвсю середину

Для двух элементов это короче и понятнее, чем numbers[0] и numbers[-1] в отдельных строках, — хотя оба варианта допустимы.

§12Когда _ не подходит

Соглашение полезно ровно до тех пор, пока оно правдиво. Три случая, где от него лучше отказаться:

СитуацияПочемуЧто делать
значение всё-таки пригодится нижечитатель уверен, что оно отброшенодать осмысленное имя
отброшено больше половины значенийстрока превращается в частокол подчёркиванийобратиться по индексу или через срез
нужен именно счётчик шагов_ обещает, что номер не используетсяfor index in range(n)

Отдельно стоит запомнить: _ и __ — разные имена, а _name с буквами — уже совсем другое соглашение, означающее «внутреннее, не для внешнего использования».

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

Что написаноЧто произойдётКак правильно
x, _ = (1, 2, 3)ValueError: значений больше, чем имёнx, _, _ = (1, 2, 3) или x, *_ = ...
использовать _ после нескольких присваиванийтам осталось только последнее значениедать имя тому, что нужно сохранить
ожидать, что _ сэкономит памятьзначение всё равно присваиваетсяэто соглашение, а не оптимизация
for _ in range(n) и print(_) в телекод работает, но противоречит заявленному намерениюfor index in range(n)
полагаться на _ из оболочки внутри скриптаNameError: автоматического результата там нетсохранять результат в обычную переменную

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

Чем _ отличается от обычной переменной?

Технически — ничем. Это соглашение между разработчиками: имя сообщает, что значение не будет использовано.

Можно ли использовать _ несколько раз в одном присваивании?

Да. Переменная просто перезаписывается, и в ней останется последнее из отброшенных значений.

Что означает for _ in range(5)?

Цикл выполнится пять раз, а номер шага в теле не используется.

Чем *_ отличается от _?

_ отбрасывает одно значение в своей позиции, *_ — весь оставшийся набор значений.

Сколько имён нужно слева при распаковке кортежа из трёх элементов?

Три — подчёркивание занимает позицию, а не пропускает её. Иначе ValueError.

§15Шпаргалка

Пропустить значениеx, _, z = data
Пропустить остатокfirst, *_ = data
Повтор без счётчикаfor _ in range(n):
Только значения словаряfor _, value in d.items():
Часть результата функцииname, _, city = get_user()
Во включении[x for _, x in pairs]

Итог главы

Подчёркивание — обычное имя, которому соглашение придало смысл «значение не нужно». Оно применяется при распаковке, в циклах ради количества повторов, при разборе пар из items() и enumerate(), а вместе со звёздочкой отбрасывает целый остаток. Пользы от него ровно столько, сколько правды: если значение всё-таки используется, ему нужно нормальное имя.

Глава 6.7 · Игнорируемые значения: _ · Справочник по Python Автор: Макаров Максим Николаевич