나는 파이썬을 사용하여 프로그램을 작성할 때, 타입 힌트를 거의 대부분 적는 편이다.

타입 힌트를 사용하면 라이브러리를 제작하거나 협업할 때에 있어, 다른 개발자가 해당 함수 어떤 값을 넣어야 하는 지, 어떤 값이 리턴되는지를 쉽게 알 수 있다고 생각하기 때문이다. 하지만 타입 힌트를 적다보면 클래스 본인을 타입 힌트에 쓸 필요가 있을 수 있다.

하지만 아래와 같은 코드를 작성하게 되면 본래 에러가 발생하게 된다.

class Number:
  def __init__(self, number: int):
    self.__number = number

  def add(self, other: Number) -> Number:
    return Number(self.get() + other.get())

  def get(self) -> int:
    return self.__number

단순히 int 객체를 감싸는 컨테이너 클래스이다.

지금 보면 other: Number-> Number 같은 곳에서 타입힌트를 준 것이 보인다.
정상적인 코드 처럼 보이지만 실제로 실행해보면 다음과 같은 에러로그를 볼 수 있다.

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in Number
NameError: name 'Number' is not defined

간략히 말하면 Number 라는 클래스가 정의되어 있지 않다는 건데
Number 를 정의하고 있는 도중에 사용하였으니 맞는 말이긴 하다.
그렇다면 어떻게 사용해야 할까?

other: 'Number' 와 같은 형식으로 string literal 로 타입 힌트를 적어주면 에러 없이 잘 실행될 것이다.
이러한 방법은 PEP563 에서 이야기 되고 있던 듯 하고 앞으로 나올 Python 4에서는 타입힌트를 저장할 때 string 형태로 저장한다.

Python3.7에서 __future__.annotationsimport하면 어떻게 달라졌는지 볼 수 있을 것이다.

Python 3.7.0b4 (default, May 11 2018, 15:18:40) 
[GCC 7.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from typing import Tuple
>>> a: Tuple[int, int]
>>> __annotations__
{'a': typing.Tuple[int, int]}

>>> from __future__ import annotations
>>> a: Tuple[int, int]
>>> __annotations__
{'a': 'Tuple[int, int]'}

생각

이렇게 string 형태로 저장하면은 타입힌트에 있던 위와 같은 문제를 해결할 수 있지만 없는 타입, 정의되지 않을 타입이어도 아무 문제 없이 넘어간다.
PEP484의 Non Goals를 보면 Python will remain a dynamically typed language, and the authors have no desire to ever make type hints mandatory, even by convention. 같은 문구가 있다.
맞는 말이긴 하지만 나는 타입 체크가 있었으면 하기 때문에 mypy에 대해 더 알아볼 필요가 있을 듯 하다.

Reference