programing

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

kingscode 2021. 1. 14. 22:55
반응형

순환 가져 오기가없는 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의 유형 힌트는 물론 작동하지 않습니다. 주기적으로 가져 오기를 가져오고 힌트없이 내 편집기 (PyCharm)가 무엇인지 알 수 없기 때문에 main.py를 가져올 수 없습니다 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


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

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

When a type hint contains names that have not been defined yet, that definition may be expressed as a string literal, to be resolved later.

So instead of:

class Tree:
def __init__(self, left: Tree, right: Tree):
    self.left = left
    self.right = right

you do:

class Tree:
def __init__(self, left: 'Tree', right: 'Tree'):
    self.left = left
    self.right = right

Turns out my original attempt was quite close to the solution as well. This is what I'm currently using:

# 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
        ...

Note the import within if False statement that never gets imported (but IDE knows about it anyway) and using the Main class as string because it's not known at runtime.


I think the perfect way should be to import all the classes and dependencies in a file (like __init__.py) and then from __init__ import * in all the other files.

In this case you are

  1. avoiding multiple references to those files and classes and
  2. also only have to add one line in each of the other files and
  3. the third would be the pycharm knowing about all of the classes that you might use.

ReferenceURL : https://stackoverflow.com/questions/39740632/python-type-hinting-without-cyclic-imports

반응형