Несовместимый тип: __main __. TestClass; ожидаемый пример. TestClass; Передача себя для работы в другом модуле - PullRequest
3 голосов
/ 26 июня 2019

Фон

У меня есть два модуля. Модуль 1 (example) определяет TestClass. Модуль 2 (example2) определяет функции change_var, которые принимают аргумент TestClass. example имеет метод change, который вызывает change_var из example2 и передает self в качестве аргумента.

example2 использует TYPE_CHECKING из typing, чтобы циклический импорт не появлялся во время выполнения, но все же позволяет MYPY проверять типы.

При звонке на change_var из change MYPY выдает ошибку Argument 1 to "change_var" has incompatible type "__main__.TestClass"; expected "example.TestClass".

Python версия: 3.7.3, MYPY версия: 0.701

Пример кода

example.py

from example2 import change_var


class TestClass:
    def __init__(self) -> None:
        self.test_var = 1

    def change(self) -> None:
        change_var(self)

example2.py

from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from example import TestClass


def change_var(obj: "TestClass") -> None:
    obj.test_var = 2

Этот код является минимальным примером реальной проблемы, с которой я сталкиваюсь в более крупном проекте Python.

Что я ожидаю от этого

Эти типы должны совпадать, поскольку они (насколько я могу судить) одинаковы.

Моя интуиция относительно того, почему это не работает, заключается в том, что TestClass в точке вызова change_var не полностью определено? По той же причине, по которой я не могу ссылаться на TestClass как на тип внутри TestClass, я не могу передать объект TestClass в функцию, которая ожидает, что объект TestClass не сможет покинуть сам класс. Для MYPY это еще не полный класс, поэтому он использует какой-то тип заполнителя. Это только интуиция.

Вопросы

  1. В чем точно здесь проблема?
  2. Как лучше всего обойти эту общую структуру кода (два модуля, класс в одном, функция, которая переводит класс в другой, вызовы методов для функции), в то же время делая MYPY счастливым?

Я также полностью готов к рефакторингу этого примера, но я хотел бы попытаться придерживаться этой общей структуры.

1 Ответ

1 голос
/ 27 июня 2019

Это один из многих случаев отказа от обработки модуля как сценария (либо с помощью -m, -c (или --command для mypy), либо просто python …/module.py). Он работает только для тривиальных приложений, которые не заботятся о идентичности типов или функций, которые они создают. (Они также должны избегать побочных эффектов на импорт и изменяющееся глобальное состояние, но в любом случае это хорошие идеи.)

Решение, помимо «не делай этого», заключается в использовании __main__.py в пакете. Даже это не без проблем, поскольку некоторые наивные рекурсивные импортеры будут импортировать его, как если бы это был настоящий модуль.

...