파이썬 3에 다음 코드가 있습니다.
class Position:
def __init__(self, x: int, y: int):
self.x = x
self.y = y
def __add__(self, other: Position) -> Position:
return Position(self.x + other.x, self.y + other.y)
그러나 내 편집자 (PyCharm)는 기준 위치를 ( __add__
방법에서) 확인할 수 없다고 말합니다 . 반환 유형이 유형이되도록 기대하려면 어떻게해야 Position
합니까?
편집 : 이것은 실제로 PyCharm 문제라고 생각합니다. 실제로 경고 및 코드 완성에 정보를 사용합니다.
그러나 내가 틀렸다면 정정하고 다른 구문을 사용해야합니다.
답변
TL; DR : Python 4.0을 사용하는 경우 작동합니다. 3.7 이상에서 오늘 (2019)부터 미래 진술 ( from __future__ import annotations
) 을 사용 하여이 기능을 켜야합니다 .Python 3.6 이하에서는 문자열을 사용하십시오.
이 예외가 있다고 생각합니다.
NameError: name 'Position' is not defined
Position
파이썬 4를 사용하지 않는 한 주석에 사용하려면 먼저 정의해야 하기 때문 입니다.
파이썬 3.7 이상 : from __future__ import annotations
Python 3.7은 PEP 563 : 연기 된 주석 평가를 도입했습니다 . future 문을 사용하는 모듈은 from __future__ import annotations
주석을 문자열로 자동 저장합니다.
from __future__ import annotations
class Position:
def __add__(self, other: Position) -> Position:
...
이것은 Python 4.0에서 기본값이 될 예정입니다. 파이썬은 여전히 동적으로 유형이 지정된 언어이므로 런타임에 유형 검사가 수행되지 않으므로 주석을 입력해도 성능에 영향을 미치지 않아야합니다. 잘못된! 파이썬 3.7로 사용되는 입력 모듈 전에 코어에서 가장 느린 파이썬 모듈 중 하나 그래서 당신이 만약 import typing
당신이 볼 시간이 성능 증가 7까지 당신이 3.7로 업그레이드 할 때.
파이썬 <3.7 : 문자열 사용
PEP 484에 따르면 클래스 자체 대신 문자열을 사용해야합니다.
class Position:
...
def __add__(self, other: 'Position') -> 'Position':
...
Django 프레임 워크를 사용하는 경우 Django 모델도 순방향 참조를 위해 문자열을 사용하므로 친숙 할 수 있습니다 (외래 모델이 self
아직 선언되지 않았거나 아직 선언되지 않은 외국 키 정의 ). 이것은 Pycharm 및 기타 도구와 함께 작동해야합니다.
출처
여행을 절약하기 위해 PEP 484 및 PEP 563 의 관련 부품 :
앞으로 참조
형식 힌트에 아직 정의되지 않은 이름이 포함 된 경우 해당 정의는 문자열 리터럴로 표현되어 나중에 확인할 수 있습니다.
이것이 일반적으로 발생하는 상황은 정의 된 클래스가 일부 메소드의 서명에서 발생하는 컨테이너 클래스의 정의입니다. 예를 들어 다음 코드 (간단한 이진 트리 구현 시작)는 작동하지 않습니다.
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
문자열 리터럴에는 유효한 Python 표현식 (예 : compile (lit, ”, ‘eval’)은 유효한 코드 객체 여야 함)이 포함되어야하며 모듈이 완전히로드되면 오류없이 평가해야합니다. 평가되는 로컬 및 글로벌 네임 스페이스는 동일한 함수에 대한 기본 인수가 평가되는 동일한 네임 스페이스 여야합니다.
및 PEP 563 :
Python 4.0에서는 정의시 함수 및 변수 주석이 더 이상 평가되지 않습니다. 대신 문자열 형식이 각
__annotations__
사전에 유지됩니다 . 정적 형식 검사기는 동작에 차이가없는 반면 런타임에 주석을 사용하는 도구는 연기 된 평가를 수행해야합니다.…
위에서 설명한 기능은 다음 특수 가져 오기를 사용하여 Python 3.7부터 사용할 수 있습니다.
from __future__ import annotations
대신 유혹을받을 수있는 것들
A. 더미 정의 Position
클래스 정의 전에 더미 정의를 배치하십시오.
class Position(object):
pass
class Position(object):
...
이것은 제거 NameError
되고 OK처럼 보일 수도 있습니다.
>>> Position.__add__.__annotations__
{'other': __main__.Position, 'return': __main__.Position}
하지만 그렇습니까?
>>> for k, v in Position.__add__.__annotations__.items():
... print(k, 'is Position:', v is Position)
return is Position: False
other is Position: False
B. 주석을 추가하기위한 Monkey-patch :
주석을 추가하기 위해 파이썬 메타 프로그래밍 마술을 시도하고 클래스 정의를 원숭이 패치하기 위해 데코레이터를 작성할 수 있습니다.
class Position:
...
def __add__(self, other):
return self.__class__(self.x + other.x, self.y + other.y)
데코레이터는 이에 상응하는 책임이 있습니다.
Position.__add__.__annotations__['return'] = Position
Position.__add__.__annotations__['other'] = Position
적어도 옳은 것 같습니다.
>>> for k, v in Position.__add__.__annotations__.items():
... print(k, 'is Position:', v is Position)
return is Position: True
other is Position: True
아마 너무 많은 문제입니다.
결론
3.6 이하를 사용하는 경우 클래스 이름을 포함하는 문자열 리터럴을 3.7에서 사용 from __future__ import annotations
하면 작동합니다.
답변
유형을 문자열로 지정하는 것은 좋지만 기본적으로 파서를 우회하고 있다는 사실에 항상 감사드립니다. 따라서 다음 리터럴 문자열 중 하나를 잘못 입력하지 않는 것이 좋습니다.
def __add__(self, other: 'Position') -> 'Position':
return Position(self.x + other.x, self.y + other.y)
약간의 변형은 바인딩 된 typevar을 사용하는 것입니다. 적어도 typevar를 선언 할 때 문자열을 한 번만 작성해야합니다.
from typing import TypeVar
T = TypeVar('T', bound='Position')
class Position:
def __init__(self, x: int, y: int):
self.x = x
self.y = y
def __add__(self, other: T) -> T:
return Position(self.x + other.x, self.y + other.y)
답변
클래스 본문 자체를 구문 분석 할 때 ‘Position’이라는 이름을 사용할 수 없습니다. 타입 선언을 어떻게 사용하는지 모르겠지만 파이썬의 PEP 484-이 타이핑 힌트를 사용하면이 시점에서 단순히 이름을 문자열로 넣을 수 있다고 말하는 경우 가장 많이 사용해야하는 모드입니다.
def __add__(self, other: 'Position') -> 'Position':
return Position(self.x + other.x, self.y + other.y)
https://www.python.org/dev/peps/pep-0484/#forward-references를 확인 하십시오 -준수하는 도구는 클래스 이름을 풀고 사용하는 것으로 알고 있습니다. 파이썬 언어 자체는 이러한 주석을 수행하지 않습니다. 일반적으로 정적 코드 분석을위한 것이거나 런타임에 유형 검사를위한 라이브러리 / 프레임 워크를 가질 수 있지만 명시 적으로 설정해야합니다.
업데이트 파이썬 3.8로, 체크, 또한 격려-563은 파이썬 3.8로 쓸 수 있습니다 – from __future__ import annotations
주석의 평가를 연기 – 앞으로 참조하는 클래스를 간단하게 작동합니다.
답변
문자열 기반 유형 힌트가 허용되는 경우 __qualname__
항목을 사용할 수도 있습니다. 클래스 이름을 보유하며 클래스 정의 본문에서 사용할 수 있습니다.
class MyClass:
@classmethod
def make_new(cls) -> __qualname__:
return cls()
이렇게하면 클래스 이름을 바꾸더라도 유형 힌트가 수정되지 않습니다. 그러나 나는 개인적으로 스마트 코드 편집기 가이 양식을 잘 처리하지 않을 것이라고 생각합니다.