Я пытаюсь разделить свой огромный класс на два; ну, в основном в "основной" класс и миксин с дополнительными функциями, например так:
main.py
файл:
import mymixin.py
class Main(object, MyMixin):
def func1(self, xxx):
...
mymixin.py
файл:
class MyMixin(object):
def func2(self: Main, xxx): # <--- note the type hint
...
Теперь, хотя это работает нормально, подсказка MyMixin.func2
типа, конечно, не может работать. Я не могу импортировать main.py
, потому что получаю циклический импорт, и без подсказки мой редактор (PyCharm) не может сказать, что это self
такое.
Я использую Python 3.4, готов перейти на 3.5, если там есть решение.
Есть ли способ разделить мой класс на два файла и сохранить все «соединения», чтобы моя IDE по-прежнему предлагала мне автоматическое завершение и все другие полезности, которые исходят от него, зная типы?
self
, поскольку он всегда будет подклассом текущего класса (и любая система проверки типов должна иметь возможность вычислить это самостоятельно). Является лиfunc2
пытаться вызовfunc1
, который не определен вMyMixin
? Может быть (какabstractmethod
, может быть)?class Main(MyMixin, SomeBaseClass)
чтобы методы из более конкретного класса могли переопределять методы из базового классаОтветы:
Боюсь, что в целом не существует очень элегантного способа обработки циклов импорта. Вы можете либо переделать свой код, чтобы удалить циклическую зависимость, либо, если это невозможно, сделать что-то вроде этого:
# some_file.py from typing import TYPE_CHECKING if TYPE_CHECKING: from main import Main class MyObject(object): def func2(self, some_param: 'Main'): ...
TYPE_CHECKING
Константа всегдаFalse
во время выполнения, поэтому импорт не будет оцениваться, но mypy (и другие инструменты типа проверки) будут оценивать содержимое этого блока.Нам также необходимо превратить
Main
аннотацию типа в строку, эффективно объявляя ее вперед, посколькуMain
символ недоступен во время выполнения.Если вы используете Python 3.7+, мы можем, по крайней мере, отказаться от предоставления явной аннотации строки, воспользовавшись PEP 563 :
# some_file.py from __future__ import annotations from typing import TYPE_CHECKING if TYPE_CHECKING: from main import Main class MyObject(object): # Hooray, cleaner annotations! def func2(self, some_param: Main): ...
from __future__ import annotations
Импорт будет делать все подсказки типа является строками и пропустить их оценку. Это может помочь сделать наш код более эргономичным.С учетом всего сказанного, использование миксинов с mypy, вероятно, потребует немного большей структуры, чем у вас есть сейчас. Mypy рекомендует подход, который в основном
deceze
описывает то, что описывает - создать ABC, который наследуют как ваш, такMain
иMyMixin
классы. Я не удивлюсь, если вам понадобится сделать что-то подобное, чтобы сделать программу проверки Pycharm счастливой.источник
typing
, но PyCharm тоже остался доволенif False:
.__init__
typing. TYPE_CHECKING
: python.org/dev/peps/pep-0484/#runtime-or-type-checkingДля людей, которые борются с циклическим импортом при импорте класса только для проверки типа: вы, вероятно, захотите использовать прямую ссылку (PEP 484 - Type Hints):
Так что вместо:
class Tree: def __init__(self, left: Tree, right: Tree): self.left = left self.right = right
ты сделаешь:
class Tree: def __init__(self, left: 'Tree', right: 'Tree'): self.left = left self.right = right
источник
File -> Invalidate Caches
?if False:
тебя можно такжеfrom typing import TYPE_CHECKING
иif TYPE_CHECKING:
.Более серьезная проблема в том, что ваши типы изначально не в здравом уме.
MyMixin
делает жестко запрограммированное предположение, что он будет смешанMain
, тогда как он может быть смешан с любым количеством других классов, и в этом случае он, вероятно, сломается. Если ваш миксин жестко запрограммирован для смешивания в один конкретный класс, вы также можете записать методы непосредственно в этот класс, а не разделять их.Чтобы правильно сделать это с разумной типизацией,
MyMixin
следует закодировать интерфейс или абстрактный класс на языке Python:import abc class MixinDependencyInterface(abc.ABC): @abc.abstractmethod def foo(self): pass class MyMixin: def func2(self: MixinDependencyInterface, xxx): self.foo() # ← mixin only depends on the interface class Main(MixinDependencyInterface, MyMixin): def foo(self): print('bar')
источник
Оказывается, моя первоначальная попытка тоже была довольно близка к решению. Вот что я сейчас использую:
# main.py import mymixin.py class Main(object, MyMixin): def func1(self, xxx): ... # mymixin.py if False: from main import Main class MyMixin(object): def func2(self: 'Main', xxx): # <--- note the type hint ...
Обратите внимание на
if False
оператор import внутри, который никогда не импортируется (но IDE все равно знает об этом) и используетMain
класс как строку, потому что он не известен во время выполнения.источник
Я думаю, что идеальный способ - импортировать все классы и зависимости в файл (например,
__init__.py
), а затемfrom __init__ import *
во все другие файлы.В этом случае вы
источник
import *
, и все же вы можете воспользоваться этим простым подходом