Справочник по Python · модуль 6 · функции и модули
Глава 6.7
Игнорируемые значения: _
Подчёркивание — обычное имя переменной, которому по соглашению придан особый смысл: «это значение мне не нужно». Оно делает намерение видимым в местах, где часть данных приходится принять, но использовать не планируется.
- Категория
- Соглашения и распаковка
- Уровень
- Начальный
- Связанные темы
- Распаковка, циклы, включения
- Зачем это знать
- Показывать, какие значения не используются
§1Зачем помечать ненужное
Распаковка требует столько имён, сколько значений в коллекции. Даже если нужно одно, придумывать имена приходится всем.
data = (10, 20, 30) x, y, z = data print(x, z)
Читатель кода видит переменную y и невольно ищет, где она понадобится. Соглашение с подчёркиванием снимает этот вопрос сразу.
- Читаемость. Явно видно, что значение принято и намеренно отброшено.
- Меньше имён. Не приходится придумывать
tmp,unusedи подобные заглушки. - Меньше ошибок. Ненужное значение не попадёт в расчёты по невнимательности.
§2_ — это обычное имя
Никакой магии в подчёркивании нет. Это допустимый идентификатор, и Python обращается с ним как с любой другой переменной.
_ = 5 print(_)
5
Особый смысл ему придают люди, а не интерпретатор. Отсюда практическое следствие: подчёркивание не экономит память и не отменяет присваивание — оно только сообщает о намерении.
В интерактивной оболочке Python имя _ занято: туда автоматически попадает результат последнего выражения. В обычном скрипте этого не происходит.
§3Распаковка кортежа
Самое частое применение — пропустить позицию при распаковке.
data = (10, 20, 30) x, _, z = data print(x, z)
10 30
Количество имён слева по-прежнему должно совпадать с количеством значений справа: подчёркивание занимает позицию, а не пропускает её.
§4Вложенная распаковка
Структуру слева можно повторить как угодно глубоко — подчёркивание работает на любом уровне.
data = ((1, 2), (3, 4), (5, 6)) (_, y1), (_, y2), (_, y3) = data print(y1, y2, y3)
2 4 6
datetime = ((5, 19, 2008), (10, 30, "am")) (_, day, _), (hour, _, _) = datetime print(day, hour)
19 10
§5Повторное использование _
Предыдущий пример показывает важную деталь: подчёркивание встречается в одном присваивании пять раз, и это не ошибка.
(_, day, _), (hour, _, _) = datetime _ последовательно получает 5, 2008, 30, "am" и в итоге хранит последнее из них
Обычная переменная просто перезаписывается на каждом шаге. Именно поэтому подчёркивание годится только для того, что действительно не понадобится: после распаковки в нём осталось лишь последнее отброшенное значение.
§6Цикл без переменной
Когда цикл нужен ради количества повторов, а номер шага не используется, переменную цикла тоже помечают подчёркиванием.
for _ in range(3): print("привет")
привет привет привет
Это сигнал читателю: тело цикла не зависит от номера итерации. Если бы вместо _ стояло i, пришлось бы просматривать тело, чтобы в этом убедиться.
§7Часть пары в цикле
Функции вроде enumerate() выдают пару значений. Если нужна только одна половина, вторую отбрасывают.
colors = ["red", "green", "blue"] for _, color in enumerate(colors): print(color)
red green blue
_ — признак лишнего кода
Если от enumerate() нужен только элемент, сам enumerate() не нужен: достаточно for color in colors. Подчёркивание уместно там, где пару выдаёт что-то неустранимое — например, dict.items() или готовая функция.
§8Ключи и значения словаря
Метод items() всегда возвращает обе части пары, и вот здесь отбросить ненужную — нормальный приём.
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Результат функции
Функция может возвращать несколько значений сразу. Принять придётся все, использовать — не обязательно.
def get_user(): return "Аня", 25, "Москва" name, _, city = get_user() print(name, city)
Аня Москва
Функция возвращает кортеж, и его распаковка подчиняется тем же правилам, что и в §3.
§10Включения
Во включениях подчёркивание используют в обеих ролях — и как отброшенную часть пары, и как переменную-счётчик.
data = [(1, "apple"), (2, "banana"), (3, "cherry")] fruit_names = [fruit for _, fruit in data] print(fruit_names)
['apple', 'banana', 'cherry']
zeros = [0 for _ in range(5)] print(zeros)
[0, 0, 0, 0, 0]
Во втором примере _ подчёркивает главное: элемент один и тот же на каждом повторе, а range() задаёт только их количество.
§11Звёздочка и остаток
Подчёркивание сочетается со звёздочкой из главы 6.6. Запись *_ отбрасывает не одно значение, а сразу весь остаток.
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 = datafirst, *_ = datafor _ in range(n):for _, value in d.items():name, _, city = get_user()[x for _, x in pairs]Итог главы
Подчёркивание — обычное имя, которому соглашение придало смысл «значение не нужно». Оно применяется при распаковке, в циклах ради количества повторов, при разборе пар из items() и enumerate(), а вместе со звёздочкой отбрасывает целый остаток. Пользы от него ровно столько, сколько правды: если значение всё-таки используется, ему нужно нормальное имя.