Автоматическое приведение переменной к правильному типу в Python

Есть ли в Python способ реализовать приведение типов, которое автоматически выбирает правильный тип?

Скажем, у вас есть класс:

class Foo:
    foo: list[int]

    def __init__(self):
        self.foo = cast(list[int], api.get())

Вы знаете, что api.get() всегда возвращает list[int], но это странная библиотека со случайными подсказками типов, поэтому она утверждает, что это (скажем) FunkyContainerType, когда это не так. Кроме того, сегодня вам не стоит беспокоиться о создании правильного файла-заглушки для библиотеки из-за сроков.

self.foo = cast(list[int], api.get()) создает в коде много визуального шума, что также является избыточным, если тип self.foo уже определен. Более того, если переменная имеет собственный тип, вам придется импортировать этот тип во время выполнения, чтобы программа не аварийно завершилась, поэтому вы не можете поместить ее в файл-заглушку и чаще будете сталкиваться с проблемами при циклическом импорте.

Я бы предпочел использовать self.foo = cast(api.get()), который определяет тип self.foo и преобразует результат вызова API к этому типу.

Таким образом, ключевыми пожеланиями являются (1) не указывать тип избыточно, (2) ничего не менять в поведении во время выполнения, т. е. не импортировать типы во время выполнения, и (3) минимальный визуальный беспорядок.

Примечания: (1) Этот вопрос именно о том, о чем говорится. Речь идет не о копировании или преобразовании значений во время выполнения. Речь идет о подсказках типов. (2) Использование приведения заключается в предоставлении дополнительной информации средству проверки типов, которую вы знаете, но которую средство проверки типов не может вывести.

Вы знаете, что typing.cast вообще не трансформирует никаких ценностей? Это просто дополнительная аннотация ввода. Это то, чего ты хочешь?

deceze 06.08.2024 09:51

Что плохого в просто self.foo = api.get()? Возможно self.foo: list[int] = api.get()? Добавьте аннотацию api.get, чтобы вернуть -> list[int]…?

deceze 06.08.2024 09:53

«он определяет тип self.foo и преобразует результат вызова API к этому типу». это не имеет никакого смысла. Как вы думаете, что cast здесь делает? cast полезен только для того, чтобы сообщить проверяющему типы: «На самом деле это не тот тип, который вы думаете, это какой-то другой тип, и я, человек, скажу вам предположить, что это так»

juanpa.arrivillaga 06.08.2024 09:57

@deceze Да на твой первый вопрос. Предположим, я не могу изменить get(), потому что это сторонний код и он может (в общем) возвращать что угодно. Но я знаю, что оно вернется list[int]. Т.е. на самом деле это такая ситуация, когда вы хотите либо привести, либо использовать утверждения типа.

Dawn Drescher 06.08.2024 14:16

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

Dawn Drescher 06.08.2024 14:18

«Я бы предпочел использовать self.foo = cast(api.get()), который определяет тип self.foo и преобразует результат вызова API к этому типу». И как именно программа проверки типов будет определять «настоящий» тип без правильных подсказок типа и без запуска кода? По сути, вы просите его выполнить задачу времени выполнения во время проверки типов.

InSync 06.08.2024 15:31

@InSync Цель функции cast — сообщить средству проверки типов то, что вы знаете о данных, но средство проверки типов не может сделать вывод. В этом случае я знаю, что данные list[int], но средство проверки типов — нет. (Я написал множество заглушек типов для недостаточно типизированных сторонних библиотек. Это определенно еще один вариант. Но сегодня у меня возникли вопросы не об этом.)

Dawn Drescher 06.08.2024 17:18
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
7
85
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

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

# type: ignore или лучше (для Пайрайта) # pyright: ignore [reportAssignmentType] сделай свое дело.

Присвоение не меняет и не расширяет тип атрибута. (H/t juanpa.arrivillaga.)

Что также работает:

self.foo = cast(Any, api.get())

Он не меняет тип self.foo на list[int] | Any, как я ожидал, но дает желаемый результат (не меняя тип). Но сейчас мне это нравится меньше, потому что для меня это менее очевидно и, на мой взгляд, добавляет больше беспорядка.

Any по сути сигнализирует «перестаньте заботиться о типах здесь», что устраняет необходимость подсказки типов.
deceze 06.08.2024 09:55

Я думаю тебе просто нужно # type: ignore

juanpa.arrivillaga 06.08.2024 10:02

Тип self.footype(self.foo). Единственный способ изменить его тип на list — это list(self.foo). Но здесь вы говорите о подсказках типов. Подсказки типов — это всего лишь подсказки, они не изменяют фактические значения.

Jeyekomon 06.08.2024 11:12

@deceze Я тоже так думал, но попробуй. У меня это работает с Пайрайтом.

Dawn Drescher 06.08.2024 14:19

@Jeyekomon Я говорю только о подсказках по типу. Значение имеет тип list[int]; программа проверки типов просто этого не знает. Я не хочу копировать список или добавлять какие-либо другие махинации во время выполнения.

Dawn Drescher 06.08.2024 14:21

@juanpa.arrivillaga # type: ignore тоже работает! Я предпочитаю # pyright: ignore [reportAssignmentType].

Dawn Drescher 06.08.2024 14:23

Определите «работает для меня». Это будет «работать» постольку, поскольку не вызовет никаких нареканий, потому что вы там явно отключаете проверку типов…?!

deceze 06.08.2024 14:32

@deceze Под «работает у меня» я имею в виду, что хочу, чтобы Пайрайт знал, что self.foo относится к типу list[int], а не Unknown, Any, list[int] | Unknown, list[int] | Any или какой-либо другой вариации. AnyUnknown в зависимости от настроек строгости) тоже не вызовет нареканий, но мне нужна проверка типов. (cast предназначен для ситуаций, когда вы знаете что-то о данных, чего не знает Пайрайт, и хотите это сообщить. Я знаю, что self.foo — это list[int], а не Any или Unknown.)

Dawn Drescher 06.08.2024 17:14

@DawnDrescher единственное, что я хотел бы отметить, это то, что # type: ignore должен работать с любой проверкой типов, можно ли использовать обе?

juanpa.arrivillaga 06.08.2024 19:48

@juanpa.arrivillaga К сожалению, это не совсем так, но в данном проекте где-то будет конфигурация, указывающая, какая программа проверки типов используется сопровождающими проекта. Я использую Pyright, поэтому, используя только комментарий Pyright, я все равно буду видеть все другие несвязанные ошибки типа в этой строке. С обоими комментариями (если это вообще синтаксически возможно), более общий из них будет экранировать этот эффект, и я не увижу несвязанных ошибок типа, потому что Pyright также уважает # type: ignore. Может быть, у MyPy есть аналогичный синтаксис комментариев?

Dawn Drescher 06.08.2024 20:11

cast — это универсальный инструмент, распознавание mypy и ему подобных жестко запрограммировано. В особых случаях вы можете определить свою собственную функцию, подобную cast, которая ограничена определенным типом возвращаемого значения (и, следовательно, не требует специальной обработки со стороны средства проверки типов).

def as_list_int(x: Any) -> list[int]:
    return x


class Foo:
    foo: list[int]

    def __init__(self):
        self.foo = as_list_int(api.get())

Настоящая проблема здесь в том, что api.get должно подразумеваться как возвращающее list[int]. Если это невозможно (потому что он действительно может возвращать значения, отличные от list[int]), то вам, вероятно, все равно понадобится другая функция, чтобы проверить, что этот вызов возвращает list[int] (вызывая исключение, если это не так) или создает list[int] из того, что он делает. возвращаться.

Спасибо! Это решение работает! Мне нравится, что он использует типы только в тех местах, где они игнорируются во время выполнения (не нужно импортировать во время выполнения). Но тип list[int] по-прежнему указывается избыточно. Это также означает, что вам понадобится отдельная функция для каждого такого типа. Я все еще предпочитаю решение # pyright: ignore [reportAssignmentType]. (Мне кажется, что это более читабельно и менее удивительно, чем моя первоначальная cast(Any, ...) идея.)

Dawn Drescher 06.08.2024 17:09

Это избыточно только в том случае, если вам нужен вывод типа, а такие инструменты, как mypy, на самом деле не предназначены для этой цели. Если вы считаете, что api.get является полиморфным по возвращаемому значению (то есть действительно способным возвращать значение любого запрошенного вами типа), то вывод типа соответствует проверке типов. Однако api.get работает не так. Он действительно возвращает значение определенного типа, но тип возвращаемого значения этого не отражает.

chepner 06.08.2024 17:34

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