주기 가져오기 없이 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()반환 .helloC.b()이 될 입니다.hello goodbyeC.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 *다른 모든 파일에 있어요.
이 경우 당신은
- 이러한 파일 및 클래스에 대한 여러 참조를 피하고
- 또한 다른 파일 각각에 한 줄만 추가하면 됩니다.
- 세 번째는 여러분이 사용할 수 있는 모든 수업에 대해 알고 있는 pycharm이 될 것입니다.
언급URL : https://stackoverflow.com/questions/39740632/python-type-hinting-without-cyclic-imports
'programing' 카테고리의 다른 글
| 테이블 셀에서의 VueJ 이행 (0) | 2022.09.22 |
|---|---|
| JavaScript를 사용하여 숫자의 소수점 부분 가져오기 (0) | 2022.09.22 |
| 전체 프로젝트의 기호를 찾아서 바꾸시겠습니까? (0) | 2022.09.22 |
| Larabel 5의 카본에서 현재 타임스탬프를 얻는 방법 (0) | 2022.09.22 |
| ImportError: 'MySQL'이라는 이름의 모듈이 없습니다. (0) | 2022.09.22 |