Введение

Как-то раз я наткнулся на такой фрагмент кода на 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].

Так что если в процессе разработки нового инструмента или библиотеки вам понадобится литерал-заполнитель, вспомните об объекте многоточия.

Заключение

Спасибо, что дочитали статью до конца. Мы поговорили о том, что такое объект многоточия и где его можно использовать. Возможные варианты его использования таковы: аннотации типов, замена пропусков или использование для определенных функций, где вам нужен неиспользуемый литерал. Также мы рассмотрели сторонние пакеты, использующие объект многоточия для реализации специальных возможностей.

Ссылки


Скоро состоится открытое занятие «Знакомство с веб разработкой на Flask». На уроке познакомимся с основами веб-разработки на Flask, а также научимся создавать и рендерить шаблоны страниц. Попробуем создать Flask приложение, затем создать роуты и в конце обработать различные HTTP методы на Flask. Записаться можно на странице специализации Python Developer.

Комментарии (4)


  1. Gadd
    00.00.0000 00:00
    +6

    Можно ещё использовать Ellipsis в тестах в роли "какой-то объект". Некоторые параметризованные тесты содержат наборы данных, в которых только часть параметров имеет значение, для остальных можно использовать Ellipsis в роли "тут какой-то, неважно какой объект, главное что это не False и не None".

    False и None - вполне себе обычные значения для аргумента функции, поэтому их не всегда удобно использовать, когда хочется написать понятный тест.

    bool(...) даёт True, кстати, и это важное, и наверное, единственное отличие этого объекта от None.


    1. Pavgran
      00.00.0000 00:00

      bool(...) даёт True, кстати, и это важное, и наверное, единственное отличие этого объекта от None.

      Да. Остальные отличия - только в том, как эти объекты используются.
      Из документации:

      None

      This type has a single value. There is a single object with this value. This object is accessed through the built-in name None. It is used to signify the absence of a value in many situations, e.g., it is returned from functions that don’t explicitly return anything. Its truth value is false.

      Ellipsis

      This type has a single value. There is a single object with this value. This object is accessed through the literal ... or the built-in name Ellipsis. Its truth value is true.


  1. thepythonicway
    00.00.0000 00:00
    +1

    Спасибо за статью. Насколько я помню, сам Гвидо Ван Россум рекомендует использовать эллипсы, так что можно не бояться за best-practice :)


  1. charoduke
    00.00.0000 00:00

    Встретился недавно разбираясь в кодах yolov5 yolov8, это был как раз срез где вместо x[:,:, y] было x[..., y]