Как перегружать функции с несколькими необязательными аргументами?

У меня есть функция с несколькими kwargs по умолчанию. Один из них (где-то посередине) — логический переключатель, управляющий типом возвращаемого значения.

Я хотел бы создать две перегрузки для этого метода с Literal[True/False], но с сохранением значения по умолчанию.

Моя идея заключалась в следующем:

from typing import overload, Literal

@overload
def x(a: int = 5, t: Literal[True] = True, b: int = 5) -> int: ...

@overload
def x(a: int = 5, t: Literal[False] = False, b: int = 5) -> str: ...

def x(a: int = 5, t: bool = True, b: int = 5) -> int | str:
    if t:
        return 5
    return "asd"

Но mypy вызывает:

error: Overloaded function signatures 1 and 2 overlap with incompatible return types

Я предполагаю, что это потому, что x() будет конфликтовать.

Но я не могу удалить значение по умолчанию = False во второй перегрузке, так как ему предшествует arg a со значением по умолчанию.

Как я могу перегрузить это правильно, чтобы

  • x()int
  • x(t=True)int
  • x(t=False)str

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

mkrieger1 17.05.2022 15:08

@ mkrieger1, хотя это решило бы эту игрушечную проблему. Я просто хочу добавить типизацию в довольно известный пакет, который на данный момент не типизирован, и об изменении интерфейса только из-за проблем с типизацией не может быть и речи :)

M. Niklas 17.05.2022 15:10

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

hintze 17.05.2022 15:22
Анализ настроения постов в Twitter с помощью Python, Tweepy и Flair
Анализ настроения постов в Twitter с помощью Python, Tweepy и Flair
Анализ настроения текстовых сообщений может быть настолько сложным или простым, насколько вы его сделаете. Как и в любом ML-проекте, вы можете выбрать...
7 лайфхаков для начинающих Python-программистов
7 лайфхаков для начинающих Python-программистов
В этой статье мы расскажем о хитростях и советах по Python, которые должны быть известны разработчику Python.
Установка Apache Cassandra на Mac OS
Установка Apache Cassandra на Mac OS
Это краткое руководство по установке Apache Cassandra.
Сертификатная программа "Кванты Python": Бэктестер ансамблевых методов на основе ООП
Сертификатная программа "Кванты Python": Бэктестер ансамблевых методов на основе ООП
В одном из недавних постов я рассказал о том, как я использую навыки количественных исследований, которые я совершенствую в рамках программы TPQ...
Создание персонального файлового хранилища
Создание персонального файлового хранилища
Вы когда-нибудь хотели поделиться с кем-то файлом, но он содержал конфиденциальную информацию? Многие думают, что электронная почта безопасна, но это...
Создание приборной панели для анализа данных на GCP - часть I
Создание приборной панели для анализа данных на GCP - часть I
Недавно я столкнулся с интересной бизнес-задачей - визуализацией сбоев в цепочке поставок лекарств, которую могут просматривать врачи и...
3
3
62
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Это старая проблема. Причина в том, что вы указываете значение по умолчанию в обеих ветвях, поэтому x() возможен в обеих, а тип возврата не определен.

У меня есть следующий шаблон для таких случаев:

from typing import overload, Literal

@overload
def x(a: int = 5, t: Literal[True] = True, b: int = 5) -> int: ...

@overload
def x(a: int = 5, *, t: Literal[False], b: int = 5) -> str: ...

@overload
def x(a: int, t: Literal[False], b: int = 5) -> str: ...

def x(a: int = 5, t: bool = True, b: int = 5) -> int | str:
    if t:
        return 5
    return "asd"

Почему и как? Вы должны подумать о способах вызова вашей функции. Сначала вы можете указать a, затем t можно указать как kwarg (#2) или arg (#3). Вы также можете оставить a по умолчанию, тогда t всегда будет kwarg (снова № 2). Это необходимо, чтобы не ставить arg после kwarg, то есть SyntaxError. Перегрузка по более чем одному параметру сложнее, но возможна и таким образом:

@overload
def f(a: int = 1, b: Literal[True] = True, c: Literal[True] = True) -> int: ...

@overload
def f(a: int = 1, *, b: Literal[False], c: Literal[True] = True) -> Literal['True']: ...

@overload
def f(a: int = 1, *, b: Literal[False], c: Literal[False]) -> Literal['False']: ...

@overload
def f(a: int, b: Literal[False], c: Literal[True] = True) -> Literal['True']: ...

@overload
def f(a: int, b: Literal[False], c: Literal[False]) -> Literal['False']: ...

def f(a: int = 1, b: bool = True, c: bool = True) -> int | Literal['True', 'False']:
    return a if b else ('True' if c else 'False')  # mypy doesn't like str(c)

Можно поиграться с перегрузкой здесь.

Это точно решение, спасибо!

M. Niklas 17.05.2022 20:35

Другие вопросы по теме