Программы
Новое в Python 3.8: строго-позиционные (position-only) параметры функций

Новое в Python 3.8: строго-позиционные (position-only) параметры функций

Очередное синтаксическое новшество, которое нам представит Python 3.8 - параметры функций, которые можно будет использовать только позиционно.

В дополнении к "моржовому оператору" в Python 3.8 появится ещё один специальный символ для описания параметров функции. Если до этого мы имели дело со звёздочкой (*), то теперь ещё получим косую черту/слеш (/).

Напомню, что звёздочка как параметр функции используется для указания начала списка именованных (обязательно) параметров. То есть при вызове такой функции будет необходимо все последующие аргументы передавать с указанием их имени. Например,

def get_statistic(stat_data, *, user, date_from=None, date_to=None):
    """Получение статистики по пользователю с фильтром по дате"""

Таким образом мы обязаны передать параметр stat_data (данные для фильтрации), а также передать юзера как именованный аргумент. Остальные – также указываются как именованные аргументы, но являются необязательными:

>>> get_statistic([], user='user_login')
>>> get_statistic([], user='user_login', date_from='2019-05-01')
>>> get_statistic([], 'user_login')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: get_statistic() takes 1 positional argument but 2 were given

– довольно полезно и наглядно, когда параметров много. Например, для указания параметров фильтрации, где потенциально их может быть очень много.

Аналогично работает /. Однако, все параметры, которые шли до него, обязаны быть позиционными. То есть, если мы опишем функцию:

def contains(collection, key, /):
    """Поиск элемента в коллекции"""

– то мы не сможем вызвать эту функцию как contains([1, 2, 3], key=2), а только contains([1, 2, 3], 2). К слову, посмотрите, как описан метод __contains__ объекта range уже сейчас:

>>> help(range)

Help on class range in module builtins:

class range(object)
 |  range(stop) -> range object
...
 |  __contains__(self, key, /)
 |      Return key in self.
...

Такой синтаксис уже давно используется для Python-функций, написанных на Си. Теперь так можно будет писать прямо в Python (на момент 3.8.0a3 уже есть такая возможность).

PEP по этой новой синтаксической возможности довольно интересен. Особенно часть "Rejected Ideas". Однако, сейчас хочется больше рассмотреть раздел с мотивацией.

Помимо более жёсткого прибивания гвоздями вариантов вызова функций... и консистентности языка в плане "как в хелпе сишных функций"... Пожалуй, логичным выглядит довод с параметрами, которые имеют смысл как последовательность. Например,

range(stop=5, start=0, step=2)
range(stop=5, step=2, start=0)
range(step=2, start=0, stop=5)
range(step=2, stop=5, start=0)

– и да, range не примет и так позиционные аргументы, но порядок старт-финиш-шаг привычен. Изменение его (если бы это можно было сделать) могло бы запутать. Правда, таких "злобных буратин" надо ещё поискать... Тем не менее, это уже имеет определённый смысл. Ожидаем в Python 3.8!

И всё же, молю пользоваться новыми возможностями с осторожностью: не у всякого выдержит психика подобный код:

def name(
    positional_only_parameters,
    /,
    positional_or_keyword_parameters,
    *,
    keyword_only_parameters
):

А если ещё без форматирования, да с type-hinting-ом, да дефолтными значениями...

Изображение Python 3.11. Что нового?