Введение
Как-то раз я наткнулся на такой фрагмент кода на Python:
class CustomException(Exception):
...
Сначала я решил, что это просто какой-то псевдокод. В Python для заполнения тела пустой функции, которая еще не реализована, обычно используется ключевое слово pass
. Я подумал: «здесь указано, что далее будет больше кода, но в этом месте код сокращен, чтобы просто понять идею». Следовательно, использование ключевого слова pass
означало бы, что что-то еще не реализовано. Больше я об этом не задумывался, но иногда использовал сам, чтобы объяснить коллегам определенную концепцию.
И вот я снова наткнулся на этот элемент, на этот раз в работающем коде. Любопытство подстегнуло меня выяснить, действительно ли это такая особенность языка или просто побочный эффект чего-то другого. Это был первый раз, когда я идентифицировал объект многоточие (Ellipsis) в Python.
В этой статье мы подробнее рассмотрим объект многоточия, где можно использовать его в повседневной практике программиста, и какие известные пакеты Python используют его.
Примечание: Код, представленный в этой статье, был написан для Python 3.9 (CPython).
Общая информация
Прежде чем перейти к возможным вариантам использования, давайте для начала выясним, что представляет собой объект многоточия. По сути, это встроенная константа [1], подобная True и False:
>>> True
True
>>> Ellipsis
Ellipsis
>>> ...
Ellipsis
Точнее говоря, встроенная константа – это Ellipsis, а ... (три точки) – соответствующий литерал. Его можно также обнаружить как определенный токен в Lib/token.py [2]:
# previous code in token.py
ELLIPSIS = 52
# subsequent code in token.py
В документации сказано, что объект Ellipsis – это «специальное значение, используемое в основном в сочетании с расширенным синтаксисом среза (slice). [1] Однако я не нашел случая использования среза, несмотря на его использование в стороннем пакете NumPy [3]. В дополнение к сказанному, если вы хотите реализовать определенную функцию и вам нужен неиспользуемый литерал, можно использовать объект многоточия.
Аннотации типов
Я довольно быстро нашел сценарий, в котором уже использовал объект многоточия: аннотации типов. Для иллюстрации этого концепта предположим, что у нас есть функция, которая принимает ноль аргументов и возвращает кортеж (tuple):
def return_tuple() -> tuple:
pass
Если вы хотите дать пользователю функции больше информации, вы также можете указать, что кортеж содержит. Например, можно вернуть кортеж, состоящий из двух целых чисел:
def return_tuple() -> tuple[int, int]:
pass
Примечание: Использование tuple[int, int]
в качестве аннотации типа не поддерживается до версии Python 3.9. В предыдущих версиях Python используйте Tuple из модуля typing
или import future.annotations
.
Как аннотировать кортеж целых чисел, который может иметь любую длину больше нуля? Просто используйте многоточие:
def return_tuple() -> tuple[int, ...]:
pass
И еще кое-что. Предположим, есть функция, возвращающая callable. Если сигнатура callable может меняться, можно использовать объект многоточия, чтобы указать на это:
from typing import Callable
def return_callable() -> Callable[..., int]:
pass
Приведенный фрагмент кода означает, что возвращается callable с неизвестной сигнатурой, но тип возвращаемого callable значения указан: это целое число (integer).
Отлично! На этом этапе мы определили первый сценарий, в котором можно использовать объект многоточия в коде Python. Давайте посмотрим, как можно им воспользоваться при реализации классов и функций.
Замена ключевого слова Pass
В начале статьи уже был намек на то, что объект Ellipsis можно использовать в качестве замены ключевого слова pass
. Напоминаю, что ключевое слово pass
используется, если нужно определить, например, функцию, класс, цикл for, но пока без его реализации. Поскольку синтаксис Python требует отступа после этих конструкций, ключевое слово pass
используется, с одной стороны, для выполнения требований синтаксиса Python, и с другой стороны, для удовлетворения ваших потребностей, поскольку вам пока не нужно реализовывать эти конструкции. Вот пример:
def my_function():
pass
По сути, даже ключевое слово pass
не требуется. Нужно только записать выражение. Это может быть число:
def my_function():
1
... или сам знак многоточия:
def my_function():
...
Лично мне нравится использовать ключевое слово pass
, чтобы указать, что что-то реализовано именно так, как я хочу. Примером может служить создание пользовательского исключения без добавления дополнительных деталей:
class CustomException(Exception):
pass
С другой стороны, использование объекта Ellipsis может указывать на то, что определенная функция еще не полностью реализована, но вызывать исключение NotImplemented
кажется излишеством. Подведем итог: объект Ellipsis можно использовать в качестве замены ключевого слова pass
, но делать этого не стоит, потому что есть определенная причина, по которой все используют pass
. Однако в порядке вещей должно быть его использование его в статьях, чтобы указать, что далее есть строки кода.
Объект Ellipsis на практике
Как уже говорилось ранее, если вы хотите реализовать определенную функцию, где вам нужен неиспользуемый литерал, можно использовать объект многоточия. Typer [4] – пакет для реализации интерфейсов командной строки и FastAPI [5] (веб-фреймворк) являются яркими примерами его использования. В FastAPI он используется для того, чтобы сделать параметры обязательными [6], а в Typer он довольно похож: указывает на то, что требуется аргумент командной строки [7].
Так что если в процессе разработки нового инструмента или библиотеки вам понадобится литерал-заполнитель, вспомните об объекте многоточия.
Заключение
Спасибо, что дочитали статью до конца. Мы поговорили о том, что такое объект многоточия и где его можно использовать. Возможные варианты его использования таковы: аннотации типов, замена пропусков или использование для определенных функций, где вам нужен неиспользуемый литерал. Также мы рассмотрели сторонние пакеты, использующие объект многоточия для реализации специальных возможностей.
Ссылки
Индексирование: Инструменты структурного индексирования | Официальная документация NumPy
Параметры запроса и валидация строк: Сделайте их обязательными
Скоро состоится открытое занятие «Знакомство с веб разработкой на Flask». На уроке познакомимся с основами веб-разработки на Flask, а также научимся создавать и рендерить шаблоны страниц. Попробуем создать Flask приложение, затем создать роуты и в конце обработать различные HTTP методы на Flask. Записаться можно на странице специализации Python Developer.
Комментарии (4)
thepythonicway
00.00.0000 00:00+1Спасибо за статью. Насколько я помню, сам Гвидо Ван Россум рекомендует использовать эллипсы, так что можно не бояться за best-practice :)
charoduke
00.00.0000 00:00Встретился недавно разбираясь в кодах yolov5 yolov8, это был как раз срез где вместо x[:,:, y] было x[..., y]
Gadd
Можно ещё использовать Ellipsis в тестах в роли "какой-то объект". Некоторые параметризованные тесты содержат наборы данных, в которых только часть параметров имеет значение, для остальных можно использовать Ellipsis в роли "тут какой-то, неважно какой объект, главное что это не False и не None".
False и None - вполне себе обычные значения для аргумента функции, поэтому их не всегда удобно использовать, когда хочется написать понятный тест.
bool(...) даёт True, кстати, и это важное, и наверное, единственное отличие этого объекта от None.
Pavgran
Да. Остальные отличия - только в том, как эти объекты используются.
Из документации: