Тип подсказки с круговыми зависимостями между файлами

1

Скажем, у вас есть что-то вроде этого:

class Point:
    def compute_line(self, point: 'Point') -> 'Line':
        # computes line connecting self to point
        ...


class Line:
    def compute_intersection(self, line: 'Line') -> 'Point':
        # computes point formed from intersection of self and line
        ...

PyCharm может анализировать штриховые подсказки для автоматического завершения для выходов любого из этих методов.

Но что, если я хочу поместить Point и Line в отдельные файлы? Есть ли способ получить автозаполнение?

  • 0
    После того, как вы поместили их в разные файлы, как вы получаете к ним доступ? Если вы просто выполняете import linemod и используете linemod.Line , то вы должны аннотировать как 'linemod.Line' , а не просто 'Line' .
  • 0
    @abarnert Если я добавлю import line в point.py и добавлю import point в line.py , я получу ошибку из-за циклического импорта.
Показать ещё 1 комментарий
Теги:
python-3.x
pycharm

2 ответа

1
Лучший ответ

Проблема заключается в том, что Line не является именем типа вообще внутри point.py, а Point не является именем для типа в line.py Ввод его в кавычки не помогает; что просто задерживается, когда строка разрешена. Он по-прежнему должен в конечном итоге разрешить тип, поэтому вы просто задерживаете вещи до такой степени, что они NameError на NameError, что не помогает.

Если вы выполняете import line в Point и import point в Line, то point.Point и line.Line становятся типами, которые решают эту проблему. Но, конечно, это создает новую проблему: круговой импорт.


В некоторых случаях, как объяснено в PEP 484, вы можете решить это, просто выполнив условный импорт "статической типизации", например:

import typing
if typing.TYPE_CHECKING:
    import line

... и затем используя 'line.Line' в аннотации типа.

Подробнее см. В документах TYPE_CHECKING. (В частности, если вам нужна совместимость с Python 3.5 до 3.5.2, это даст вам NameError вместо False во время выполнения, что является болью... но не так много людей должны запускаться на 3.5.1.)


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

Например, вы можете пойти с традиционным решением "интерфейса", где первый тип зависит от второго, но второй не зависит от первого, он зависит только от суперкласса для первого.

Как правило, этот суперкласс был бы ABC, чтобы указать, что он служит только как интерфейс для какого-то реального класса, определенного в другом месте. (И таким образом, вы знаете, и Python и ваш статический контролер могут обеспечить соблюдение, что все, что является типом ABC, должно быть экземпляром одного из конкретных подклассов ABC, из которых только один.)

# pointbase.py
import abc
class PointBase(abc.ABC):
    @abc.abstractmethod
    def method_that_does_not_need_line(self):
        pass

# point.py
import pointbase
import line
class Point(pointbase.PointBase):
    def method_that_does_not_need_line(self):
        do_stuff()
    def method_that_does_need_line(self, line: line.Line):
        do_stuff(line)

# line.py
import pointbase
class Line:
    def method_that_needs_point(self, point: pointbase.PointBase):
        do_stuff(point)
  • 0
    Спасибо, я искал что-то вроде импорта только статической типизации.
0

У вас может быть утверждение для этого типа, PyCharm распознает его как тип, который вы ему утверждали.

assert isinstance(point, Point)
  • 0
    Да, но тогда нет смысла в конце типа возврата. Я надеюсь использовать конечный тип возврата для моего желаемого поведения. Кроме того, если конечный тип возврата является функцией типов входных данных метода, я не могу легко сгенерировать утверждение.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню