0

Как задать тип возвращаемого значения функции, зависящий от типа входного аргумента?

12

Проблема с типами в функции маппинга данных для Postgres в Python

У меня есть функция, которая конвертирует типы данных Python в типы данных Postgres следующим образом:

def map_type(input):
    if isinstance(input, int):
        return MyEnum(input)
    elif isinstance(input, str):
        return MyCustomClass(str)

Я мог бы указать намек на тип для этой функции так:

def map_type(input: Union[int, str]) -> Union[MyEnum, MyCustomClass]: ...

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

myvar = map_type('foobar')
print(myvar.property_of_my_custom_class)

Вот полный пример (рабочий код, но с ошибками в аннотации типов):

from typing import Union
from enum import Enum

class MyEnum(Enum):
    VALUE_1 = 1
    VALUE_2 = 2

class MyCustomClass:

    def __init__(self, value: str) -> None:
        self.value = value

    @property
    def myproperty(self) -> str:
        return 2 * self.value

def map_type(value: Union[int, str]) -> Union[MyEnum, MyCustomClass]:
    if isinstance(value, int):
        return MyEnum(value)
    elif isinstance(value, str):
        return MyCustomClass(value)
    raise TypeError('Invalid input type')

myvar1 = map_type(1)
print(myvar1.value, myvar1.name)

myvar2 = map_type('foobar')
print(myvar2.myproperty)

Я понимаю, что мог бы разделить маппинг на две функции, но цель состоит в том, чтобы иметь универсальную функцию для преобразования типов.

Также я задумался о работе с классами и полиморфизмом, но тогда как мне аннотировать методы верхнего класса? Поскольку их выходной тип будет зависеть от конкретного типа экземпляра.

Как лучше всего решить данную проблему с типами?

1 ответ(ов)

0

Для выходных типов, которые идентичны или являются вариацией входного типа, вы можете использовать следующую стратегию (и избежать необходимости добавления дополнительных @overload при введении/поддержке новых типов):

from typing import TypeVar

T = TypeVar("T")  # имя переменной должно совпадать со строкой

def filter_category(category: T) -> list[T]:
    # предположим, что get_options() — это функция, 
    # которая получает произвольное количество объектов различных типов
    return [
        option 
        for option in get_options() 
        if isinstance(option, category)  # исправлено на isinstance
    ]

Функция filter_category будет правильно ассоциироваться как с вашим IDE (например, VSCode, PyCharm и т.д.), так и со статическим анализатором типов (таким как Mypy):

# например, list_of_ints будет показан в вашем IDE как тип list[Type[int]]
list_of_ints = filter_category(int)

# или здесь list_of_bools будет показан в вашем IDE как тип list[Type[bool]]
list_of_bools = filter_category(bool)

Примечание: это не охватывает все аспекты вопроса, только его подмножество, хотя и очень распространенное.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь