programing

주기 가져오기 없이 Python 유형 암시

sourcejob 2022. 9. 22. 00:17
반응형

주기 가져오기 없이 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힌트가 는 Import의 내용을 알 수self empty.month.mpti.

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실행 시 Import는 평가되지 않지만 mypy(및 기타 유형 검사 도구)는 해당 블록의 내용을 평가합니다.

VIP도 돼요.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 annotationsimport는 모든 유형의 힌트를 문자열로 만들고 평가를 건너뜁니다.이것은 우리의 코드를 약간 더 인체공학적으로 만드는 데 도움이 될 수 있다.

즉, mypy에 mixin을 사용하는 것은 현재보다 조금 더 많은 구조를 필요로 할 것입니다.Mypy는 기본적으로 다음과 같은 접근 방식을 권장합니다.deceze하고 있습니다. 를 하려면 , 양쪽 의 ABC 가 합니다.- 쪽 is 。Main ★★★★★★★★★★★★★★★★★」MyMixin클래스가 상속됩니다.파이캄의 체커를 기쁘게 하기 위해 당신이 비슷한 일을 해야 한다고 해도 나는 놀라지 않을 것이다.

유형 검사 전용 클래스를 가져올 때 주기적인 가져오기에 어려움을 겪는 사용자의 경우: 정방향 참조(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단, 다른 클래스에는 얼마든지 혼재할 수 있으며, 이 경우 아마 깨질 수 있습니다.의 특정되어 있는 에 직접 쓰는 .mixin은 다른 클래스로 분류할 수 .

타이핑으로 하려면 , 「이렇게 해 주세요」를 참조해 주세요.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
        ...

「 Import 」내의 .if False 않는 는 이 ) 및 Import를 하는 스테이트먼트(IDE)Main실행 시 알 수 없으므로 클래스가 문자열로 지정됩니다.

Python 3.5 이후로는 클래스를 별도의 파일로 분할하는 것이 쉽습니다.

쓸수 있어요.import내부의 진술class ClassName:블록하다를 들어 '예: '예: '예:

class_def.py:

class C:
    from _methods1 import a
    from _methods2 import b

    def x(self):
        return self.a() + " " + self.b()

제 예에서는

  • C.a() 반환 .hello
  • C.b()이 될 입니다.hello goodbye
  • C.x()이렇게 해서 돌아오다hello hello goodbye.

a ★★★★★★★★★★★★★★★★★」b , , , , 을.

_methods1.py:

from __future__ import annotations
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from class_def import C

def a(self: C):
    return "hello"

설명:TYPE_CHECKINGTrue체커는가 없기 순환 Import하다.if TYPE_CHECKING: ㅇㅇㅇㅇㅇ.__future__import는 연기된 주석을 활성화합니다.이것은 옵션입니다.선택사항이 없으면 유형 주석(즉, 주석)을 따옴표로 묶어야 합니다.def a(self: "C"):를 참조해 주세요.

「 」를 합니다._methods2.py츠키다

from __future__ import annotations
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from class_def import C

def b(self: C):
    return self.a() + " goodbye"

코드에서는 VS 코드로부터 할 수 있습니다.self.a()호버링 시:

모든 것이 예상대로 실행됩니다.

>>> from class_def import C
>>> c = C()
>>> c.x()
'hello hello goodbye'

이전 Python 버전에 대한 참고 사항

버전 Python '33.4',TYPE_CHECKING정의되어 있지 않기 때문에, 이 솔루션은 동작하지 않습니다.

Python의 33.6은 Python입니다.삼다를 합니다.from __future__ import annotations그리고 위에서 언급한 형식 선언을 인용합니다.

typing.TYPE_CHECKING 원형 즉, 원형 활자를 사용하지 말 것, 즉, 세나니건을 사용하지 않는 입니다.from및 중 하나를 합니다.from __future__ import annotations또는 문자열 주석입니다.

# foo.py
from __future__ import annotations
import bar


class Foo:
    bar: bar.Bar
# bar.py
import foo


class Bar:
    foo: "foo.Foo"

되고 만, 「가져오기」를 사용하고 있습니다.from foo import Foo이 Python 합니다.foo최종값을 얻기 위한 모듈Foo바로 수입 라인에 있습니다.실행 시에도 사용할 필요가 있는 경우, 예를 들어 다음과 같이 매우 편리합니다.foo.Foo또는bar.Bar기능/기능을 한 번만 호출해야 하므로 기능/기능 내에서 사용해야 합니다.foo.Foo그리고.bar.Bar사용할 수 있습니다.

다른 사람들이 제안한 것처럼 코드를 리팩터링하는 것이 좋습니다.

최근 발생한 순환 오류를 보여 줄 수 있습니다.

이전:

# person.py
from spell import Heal, Lightning

class Person:
    def __init__(self):
        self.life = 100

class Jedi(Person):
    def heal(self, other: Person):
        Heal(self, other)

class Sith(Person):
    def lightning(self, other: Person):
        Lightning(self, other)

# spell.py
from person import Person, Jedi, Sith

class Spell:
    def __init__(self, caster: Person, target: Person):
        self.caster: Person = caster
        self.target: Person = target

class Heal(Spell):
    def __init__(self, caster: Jedi, target: Person):
        super().__init__(caster, target)
        target.life += 10

class Lightning(Spell):
    def __init__(self, caster: Sith, target: Person):
        super().__init__(caster, target)
        target.life -= 10

# main.py
from person import Jedi, Sith

단계별:

# main starts to import person
from person import Jedi, Sith

# main did not reach end of person but ...
# person starts to import spell
from spell import Heal, Lightning

# Remember: main is still importing person
# spell starts to import person
from person import Person, Jedi, Sith

콘솔:

ImportError: cannot import name 'Person' from partially initialized module
'person' (most likely due to a circular import)

스크립트/모듈은 1개의 스크립트만 Import할 수 있습니다.

그 후:

# person.py
class Person:
    def __init__(self):
        self.life = 100

# spell.py
from person import Person

class Spell:
    def __init__(self, caster: Person, target: Person):
        self.caster: Person = caster
        self.target: Person = target

# jedi.py
from person import Person
from spell import Spell

class Jedi(Person):
    def heal(self, other: Person):
        Heal(self, other)

class Heal(Spell):
    def __init__(self, caster: Jedi, target: Person):
        super().__init__(caster, target)
        target.life += 10

# sith.py
from person import Person
from spell import Spell

class Sith(Person):
    def lightning(self, other: Person):
        Lightning(self, other)

class Lightning(Spell):
    def __init__(self, caster: Sith, target: Person):
        super().__init__(caster, target)
        target.life -= 10

# main.py
from jedi import Jedi
from sith import Sith

jedi = Jedi()
print(jedi.life)
Sith().lightning(jedi)
print(jedi.life)

실행된 행의 순서:

from jedi import Jedi  # start read of jedi.py
from person import Person  # start AND finish read of person.py
from spell import Spell  # start read of spell.py
from person import Person  # start AND finish read of person.py
# finish read of spell.py

# idem for sith.py

콘솔:

100
90

파일 구성이 핵심입니다. 도움이 되기를 바랍니다.D

파일 내의 모든 클래스 및 종속성을 Import하는 것이 가장 좋은 방법이라고 생각합니다(예:__init__.py)을 클릭합니다.from __init__ import *다른 모든 파일에 있어요.

이 경우 당신은

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

언급URL : https://stackoverflow.com/questions/39740632/python-type-hinting-without-cyclic-imports

반응형