Использование программно созданного типа в подсказках по типам

По некоторым внешним причинам я динамически генерирую набор классов данных с помощью make_dataclass. В других частях моей кодовой базы я хочу использовать эти типы в подсказках типов. Но и mypy, и pyright жалуются.

$ pyright dynamic_types.py
/home/user/testing/dynamic_types.py
  /home/user/testing/dynamic_types.py:23:18 - error: Variable not allowed in type expression (reportInvalidTypeForm)
1 error, 0 warnings, 0 informations 
$ mypy dynamic_types.py
dynamic_types.py:23: error: Variable "dynamic_types.mydc" is not valid as a type  [valid-type]
dynamic_types.py:23: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases
Found 1 error in 1 file (checked 1 source file)

Я понимаю аргумент, но в моем случае «динамическая» часть — это словарь внутри того же модуля. Есть ли способ заставить это работать?

МВЕ:

from dataclasses import asdict, field, make_dataclass


class Base:
    def my_method(self):
        pass


spec = {
    "kind_a": [("foo", int, field(default=None)), ("bar", str, field(default=None))]
}


def make_mydc(kind: str) -> type:
    """Create dataclass from list of fields and types."""
    fields = spec[kind]
    return make_dataclass("mydc_t", fields, bases=(Base,))


mydc = make_mydc("kind_a")


def myfunc(data: mydc):
    print(asdict(data))


data = mydc(foo=42, bar = "test")
myfunc(data)

ПРИМЕЧАНИЕ. Я не могу использовать Base для ввода подсказки, поскольку у нее нет всех атрибутов.

Вы можете написать плагин Mypy, но он не будет работать для других средств проверки типов. Статическая типизация должна быть статичной.

InSync 28.06.2024 10:12

@InSync спасибо за эту альтернативу, я ее рассмотрю. Возможно, это приемлемый вариант, поскольку в CI мы используем только mypy.

suvayu 28.06.2024 21:33
Почему в 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
2
62
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

Большинство средств проверки статических типов работают на уровне AST: они преобразуют текстовое представление программы в AST и выводят типы оттуда. Это работает, когда типы известны заранее, например. г.:

from typing import Union
a: int = 7
b: Union[int, str] = 3 if a < 5 else 'some string'

Правильность этих аннотаций типов можно проверить, только взглянув на AST. Однако str также будет допустимой аннотацией типа для b, потому что эта строка всегда будет иметь значение str. Но для того, чтобы прийти к такому выводу, проверяющему типы необходимо будет проанализировать, что на самом деле происходит, одного взгляда на AST недостаточно. Вам нужна какая-то интерпретация.

Интерпретатор Python, конечно, интерпретирует код, запуская его. Но вы также можете сделать это статически, т.е. е. анализируя код без его запуска.

Для этого вам придется пойти на один уровень глубже и принять во внимание граф потока управления (и не только AST). Чтобы получить полный граф потока управления, вам необходимо абстрактно интерпретировать код Python.

(Абстрактная интерпретация выполняется довольно медленно (слишком много возможных путей программы), тогда как средства проверки типов, которые смотрят только на AST, работают быстро.)

Ваш код не создает новый тип класса данных с помощью методов AST (нет ClassDef или аналогичного узла AST), а вместо этого вызывает функцию Python, которая использует больше кода Python, который в конечном итоге вызывает __prepare__ и т. д. Чтобы определить, что make_mydc возвращает весь этот код, нужен интерпретировать абстрактно.

Ваш пример кода кажется мне выполнимым (т. е. может быть проверен тип в разумные сроки). Но он замедлится, если вы вызовете myfunc из более сложного кода.

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

Спасибо за очень четкий ответ, я боялся, что это так :(. Я делал это динамически, чтобы избежать дублирования, потому что мне приходилось поддерживать сопоставление с чем-то внешним, что на самом деле не под моим контролем. Но, возможно, это неизбежно .

suvayu 28.06.2024 21:32

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