巨大なクラスを2つに分割しようとしています。基本的に、「メイン」クラスと、次のような追加機能を備えたミックスインです。
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に移行することを望んでいます。
クラスを2つのファイルに分割し、すべての「接続」を保持して、IDEが自動補完とタイプを知っている他のすべての利点を引き続き提供できるようにする方法はありますか?
一般的に、インポートサイクルを処理するための非常にエレガントな方法はありません。選択肢は、周期的な依存関係を削除するようにコードを再設計するか、実行できない場合は次のようにします。
# 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 56 を利用して、明示的な文字列注釈を提供する必要をスキップできます。
# 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
importは、allタイプヒントを文字列にし、評価をスキップします。これは、ここでのコードを少し人間工学に基づいたものにするのに役立ちます。
とはいえ、mypyでmixinを使用するには、現在よりも少し多くの構造が必要になる可能性があります。 Mypy アプローチを推奨 これは基本的にdeceze
が記述するものです-Main
クラスとMyMixin
クラスの両方が継承するABCを作成します。 Pycharmのチェッカーを幸せにするために、似たようなことをする必要が生じたとしても、私は驚かないでしょう。
大きな問題は、あなたの型が最初から正気でないことです。 MyMixin
は、それがMain
に混在するというハードコードされた仮定を行いますが、他のクラスに混在させることもできますが、その場合はおそらく壊れます。ミックスインが特定のクラスにミックスされるようにハードコードされている場合、メソッドを分離するのではなく、クラスに直接書き込むこともできます。
適切なタイピングでこれを適切に行うには、MyMixin
をinterface、または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')
型チェックのみのためにクラスをインポートする場合、循環インポートに苦労している人々のために: Forward Reference (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
私の最初の試みも同様に解決策に非常に近かったことがわかりました。これは私が現在使用しているものです:
# 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
ステートメント内のインポートに注意してください(ただし、IDEはそれについては知っています)。実行時に不明なので、Main
クラスを文字列として使用します。