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


110

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

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


2
나는 self항상 현재 클래스의 하위 클래스가 될 것이기 때문에 일반적으로 유형에 주석을 달 필요가 없다고 생각합니다 (모든 유형 검사 시스템은 자체적으로 알아낼 수 있어야합니다). 에 정의되지 않은 func2을 (를) 호출하려고 합니까? 아마도 (아마도) 있어야 합니까? func1MyMixinabstractmethod
Blckknght

또한 일반적으로보다 구체적인 클래스 (예 : mixin)는 클래스 정의에서 기본 클래스의 왼쪽으로 이동해야합니다. 즉 class Main(MyMixin, SomeBaseClass),보다 구체적인 클래스의 메서드 가 기본 클래스의 메서드를 재정의 할 수 있도록해야합니다.
Anentropic

3
이 코멘트가 질문에 접하는 것이기 때문에 어떻게 유용한 지 잘 모르겠습니다. 벨리스는 코드 리뷰를 요청하지 않았습니다.
Jacob Lee

가져온 클래스 메서드 가있는 Python 유형 힌트는 문제에 대한 우아한 솔루션을 제공합니다.
Ben Mares

답변:


168

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

# 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


4
감사합니다. 내 현재 파이썬 3.4 typing에는는 없지만 PyCharm도 매우 만족했습니다 if False:.
velis

유일한 문제는 MyObject를 Django models.Model로 인식하지 못하므로 외부에서 정의되는 인스턴스 속성에 대해 잔소리를합니다__init__
velis

다음은 해당 pep입니다 typing. TYPE_CHECKING . python.org/dev/peps/pep-0484/#runtime-or-type-checking
Conchylicultor

25

유형 검사를 위해서만 클래스를 가져올 때 주기적 가져 오기로 어려움을 겪는 사람들을 위해 : 앞으로 참조 (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

PyCharm 일 수 있습니다. 최신 버전을 사용하고 있습니까? 시도해 보셨습니까 File -> Invalidate Caches?
Tomasz Bartkowiak

감사. 죄송합니다. 댓글을 삭제했습니다. 이것이 작동한다고 언급했지만 PyCharm은 불평합니다. Velis가 제안한 if False 해킹을 사용하여 해결 했습니다 . 캐시를 무효화해도 해결되지 않았습니다. 아마도 PyCharm 문제 일 것입니다.
제이콥 리

1
대신 @JacobLee if False:당신은 또한 수 from typing import TYPE_CHECKINGif TYPE_CHECKING:.
luckydonald

11

더 큰 문제는 당신의 유형이 처음부터 정상적이지 않다는 것입니다. 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')

1
글쎄, 내 솔루션이 훌륭하다고 말하는 것이 아닙니다. 코드를 더 관리하기 쉽게 만들기 위해 제가하려는 일입니다. 귀하의 제안은 통과 할 수 있지만 실제로는 특정 경우 전체 Main 클래스를 인터페이스로 이동하는 것을 의미 합니다.
velis

3

내 원래 시도도 솔루션에 매우 가깝다는 것이 밝혀졌습니다. 이것은 내가 현재 사용하고있는 것입니다.

# 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런타임에 알려지지 않았기 때문에 클래스를 문자열로 사용합니다 .


이로 인해 죽은 코드에 대한 경고가 발생할 것으로 예상됩니다.
Phil

@Phil : 예, 저는 Python 3.4를 사용하고있었습니다. 이제
타이핑이 있습니다 .TYPE_CHECKING

-4

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

이 경우 당신은

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

1
그것은 당신이 모든 곳에서 모든 것을로드한다는 것을 의미합니다. 만약 당신이 꽤 무거운 라이브러리를 가지고 있다면 그것은 모든 가져 오기에 대해 전체 라이브러리를로드해야한다는 것을 의미합니다. + 참조는 매우 느리게 작동합니다.
Omer Shacham

> 그것은 당신이 모든 곳에서 모든 것을 로딩하고 있음을 의미합니다. >>>> " init .py"또는 기타 파일 import *
Sławomir Lenart
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.