복붙노트

[PYTHON] 메소드의 반환 유형이 파이썬에서 클래스 자체와 동일한 지 어떻게 지정합니까?

PYTHON

메소드의 반환 유형이 파이썬에서 클래스 자체와 동일한 지 어떻게 지정합니까?

파이썬 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 문제라고 생각한다. 실제로 경고의 정보와 코드 완성을 사용합니다.

그러나 내가 틀렸다면 나를 수정하고 다른 구문을 사용해야합니다.

해결법

  1. ==============================

    1.TL : DR : Python 3.7 이상을 사용하는 경우 주석 모듈을 가져 오면 원하는대로 작동합니다. Python 3.6 이하에서는 문자열을 사용합니다.

    TL : DR : Python 3.7 이상을 사용하는 경우 주석 모듈을 가져 오면 원하는대로 작동합니다. Python 3.6 이하에서는 문자열을 사용합니다.

    나는이 예외가 있다고 생각한다.

    NameError: name 'Position' is not defined
    

    이는 Python 4를 사용하지 않는 한 주석을 사용하기 전에 Position을 정의해야하기 때문입니다.

    Python 3.7에는 PEP 563이 도입되었습니다. 주석 평가가 연기되었습니다. __future__ import annotations의 future 문을 사용하는 모듈은 주석을 문자열로 자동 저장합니다.

    from __future__ import annotations
    
    class Position:
        def __add__(self, other: Position) -> Position:
            ...
    

    이것은 Python 4.0에서 기본값이 될 예정입니다. 파이썬은 여전히 ​​동적 타입 언어이기 때문에 런타임에 타입 검사가 수행되지 않으므로 주석을 입력해도 성능에 영향을주지 않아야합니다. 잘못된! 파이썬 3.7 이전에는 타이핑 모듈이 코어에서 가장 느린 파이썬 모듈 중 하나 였기 때문에 타이핑을 가져 오면 3.7으로 업그레이드 할 때 성능이 최대 7 배 향상됩니다.

    PEP 484에 따르면 클래스 자체 대신 문자열을 사용해야합니다.

    class Position:
        ...
        def __add__(self, other: 'Position') -> 'Position':
           ...
    

    Django 프레임 워크를 사용한다면 Django 모델은 앞으로 참조를위한 문자열 (외국 모델이 자체이거나 아직 선언되지 않은 외래 키 정의)을 사용하기 때문에 익숙 할 것입니다. 이것은 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
    

    및 PEP 563 :

    from __future__ import annotations
    

    클래스 정의를하기 전에 더미 정의를 배치하십시오.

    class Position(object):
        pass
    
    
    class Position(object):
        ...
    

    이것은 NameError를 없애고 심지어 보일 수도 있습니다 :

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

    파이썬 메타 프로그래밍 매직을 시도하고 데코레이터 쓰기를 원할 수 있습니다. 특수 효과를 추가하려면 클래스 정의를 원숭이 패치하십시오.

    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에서 __future__ import annotations에서 사용하는 클래스 이름을 포함하는 문자열 리터럴을 사용하면 올바르게 작동합니다.

  2. ==============================

    2.'위치'라는 이름은 클래스 본문 자체가 파싱 될 때 사용할 수 없습니다. 나는 타입 선언을 어떻게 사용하는지 모르지만, Python의 PEP 484는 타이핑 힌트를 사용하는 경우 대부분의 모드에서 사용해야하는 것으로,이 시점에서 이름을 단순히 문자열로 지정할 수 있다고합니다.

    '위치'라는 이름은 클래스 본문 자체가 파싱 될 때 사용할 수 없습니다. 나는 타입 선언을 어떻게 사용하는지 모르지만, Python의 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 - 거기에있는 클래스 이름의 패키지를 풀어서 사용하도록하는 도구를 확인하십시오 (항상 사용하는 것이 중요합니다. Python 언어 그 자체는 이러한 주석의 일을하지 않는다는 것을 명심하십시오. 보통 정적 코드 분석을위한 것이거나 런타임에 유형 검사를위한 라이브러리 / 프레임 워크를 가질 수 있습니다.하지만 명시 적으로 설정해야합니다.

  3. ==============================

    3.문자열로 타입을 지정하는 것은 괜찮지 만, 우리가 기본적으로 파서를 우회하고 있다는 것을 나에게 조금은 욕되게한다. 따라서이 리터럴 문자열 중 하나를 잘못 입력하지 않는 것이 좋습니다.

    문자열로 타입을 지정하는 것은 괜찮지 만, 우리가 기본적으로 파서를 우회하고 있다는 것을 나에게 조금은 욕되게한다. 따라서이 리터럴 문자열 중 하나를 잘못 입력하지 않는 것이 좋습니다.

    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)
    
  4. from https://stackoverflow.com/questions/33533148/how-do-i-specify-that-the-return-type-of-a-method-is-the-same-as-the-class-itsel by cc-by-sa and MIT license