Скажем, у вас есть что-то вроде этого:
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
в отдельные файлы? Есть ли способ получить автозаполнение?
Проблема заключается в том, что 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)
У вас может быть утверждение для этого типа, PyCharm распознает его как тип, который вы ему утверждали.
assert isinstance(point, Point)
import linemod
и используетеlinemod.Line
, то вы должны аннотировать как'linemod.Line'
, а не просто'Line'
.import line
вpoint.py
и добавлюimport point
вline.py
, я получу ошибку из-за циклического импорта.