[python] 순환 가져 오기가없는 Python 유형 힌트

나는 나의 거대한 수업을 두 개로 나누려고 노력하고있다. 글쎄, 기본적으로 “메인”클래스에 추가하고 다음과 같이 추가 기능과 혼합합니다.

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가 여전히 자동 완성 및 유형을 알고있는 다른 모든 기능을 제공 할 수있는 방법이 있습니까?



답변

일반적으로 가져 오기주기를 처리하는 매우 우아한 방법은 없습니다. 두렵습니다. 선택 사항은 순환 종속성을 제거하기 위해 코드를 다시 설계하거나 실행 가능하지 않은 경우 다음과 같이하는 것입니다.

# 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와 함께 mixin을 사용하면 현재보다 약간 더 많은 구조가 필요할 수 있습니다. Mypy 는 기본적으로 설명 하는 접근 방식권장합니다. 즉 , 클래스 와 클래스 deceze모두 상속 하는 ABC를 만드는 것 입니다. Pycharm의 검사기를 행복하게 만들기 위해 비슷한 작업을해야한다면 놀라지 않을 것입니다.MainMyMixin


답변

유형 검사를 위해서만 클래스를 가져올 때 주기적 가져 오기로 어려움을 겪는 사람들을 위해 : 앞으로 참조 (PEP 484-유형 힌트) 를 사용하는 것이 좋습니다 .

형식 힌트에 아직 정의되지 않은 이름이 포함 된 경우 해당 정의는 나중에 해결 될 문자열 리터럴로 표현 될 수 있습니다.

그래서 대신 :

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


답변

더 큰 문제는 당신의 유형이 처음부터 정상적이지 않다는 것입니다. MyMixin혼합 될 것이라고 하드 코딩 된 가정을합니다.Main 반면, 다른 클래스와 혼합 될 수있는 반면,이 경우에는 아마도 깨질 것입니다. 믹스 인이 하나의 특정 클래스로 혼합되도록 하드 코딩 된 경우 메서드를 분리하는 대신 해당 클래스에 직접 작성하는 것이 좋습니다.

건전한 타이핑으로이 작업을 제대로 수행하려면 인터페이스 또는 Python 용어로 추상 클래스에 MyMixin대해 코딩해야합니다 .

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 within 문 (그러나 IDE는 어쨌든 그것에 대해 알고 있음)과 Main런타임에 알려지지 않았기 때문에 클래스를 문자열로 사용합니다 .


답변

완벽한 방법은 파일 (예 :)의 모든 클래스와 종속성을 가져온 __init__.py다음 from __init__ import *다른 모든 파일을 가져 오는 것 입니다.

이 경우 당신은

  1. 해당 파일 및 클래스에 대한 여러 참조를 피하고
  2. 또한 다른 파일 각각에 한 줄만 추가하면됩니다.
  3. 세 번째는 사용할 수있는 모든 클래스에 대해 알고있는 pycharm입니다.


답변