파이썬 타입 어노테이션/힌트
May 29, 2020 · 5 min read



파이썬 버전 3.5에 추가된 타입 어노테이션(type annotation) 또는 타입 힌트(type hint)가 점점 많은 파이썬 프로젝트에서 사용되고 있는 추세입니다.
정적(static) 프로그래밍 언어에서 흔히 볼 수 있는 타입 체킹(type checking)도 아니고 파이썬의 타입 힌팅(type hinting)는 도대체 무엇일까요?

파이썬은 동적 프로그래밍 언어

동적(dynamic) 프로그래밍 언어인 파이썬에서는 인터프리터(interpreter)가 코드를 실행하면서 타입(type)을 추론하여 체크합니다.
또한 파이썬에서 변수의 타입(type)은 고정되어 있지 않기 때문에 개발자가 원하면 자유롭게 바꿀 수 있습니다.

>>> no = 1
>>> type(no)
<class 'int'>
>>> no = "1"
>>> type(no)
<class 'str'>

위 코드를 보면 no 변수의 타입이 처음에는 int였다가 str으로 바뀐 것을 알 수 있습니다.
자바와 같은 정적(static) 프로그래밍 언어에서는 상상도 하기 힘든 일이며 이런 코드는 컴파일(compile)조차 되지 않습니다.

/* java */
int no = 1;
no = "1"; // compile error!!

이렇게 유연한 파이썬의 타입 처리는 타입을 명시적으로 표시할 필요가 없기 때문에 파이썬으로 우리는 다른 언어에 비해 매우 간단명료한 코드를 작성할 수 있는 것이지요.

타입 어노테이션/힌트?

타입에 대한 파이썬의 유연함은 일회성 스크립트나 소규모의 애플리케이션을 빠르게 개발할 때는 큰 장점으로 작용합니다.
하지만 애플리케이션이 규모가 커지게 되면 이러한 파이썬의 다이나믹함이 치명적인 버그로 이어질 확률이 높아지게 되며 애플리케이션 안정성에 위험 요소가 되기도 합니다.
따라서 중규모 이상의 파이썬 프로젝트에서는 소위 타입 힌팅(type hinting)이라는 개발 프로세스를 도입하여 사용하는 경우가 많습니다.

타입 힌팅(type hinting)은 말 그대로 파이썬 코드를 작성할 때 타입에 대한 메타 정보를 제공하는 것입니다.
파이썬에 타입 힌팅이 추가되기 전에는 타입을 표시하려면 주석(comments)을 이용했어야 했었습니다.
이 방법은 코드를 읽는 개발자에게는 약간의 도움이 될지는 모르겠으나 정해진 표준이 없었고 일반 주석과 구분이 어려웠습니다.

num = 1  # type: int

def repeat(message, times = 2):
    # type: (str, int) -> list
    return [message] * times

반면에 타입 힌팅에서는 타입 어노테이션(annotation)이라는 새로운 방법으로 파이썬 코드의 타입 표시를 표준화합니다.
따라서 코드 편집기(IDE)나 린터(linter)에서도 해석할 수 있도록 고안되었으며, 코드 자동 완성이나 정적 타입 체킹에도 활용되고 있습니다.

num: int = 1

def repeat(message: str, times: int = 2) -> list:
    return [message] * times

여기서 주의 사항은 타입 힌팅은 언어 레벨에서 실질적으로 어떠한 제약 사항도 강요되지 않는다는 것입니다.
다시 말해, 변수나 함수에 추가한 타입 어노테이션이 부정확한다고 해서 경고나 오류가 발생하는 것은 아닙니다.

타입 힌팅(type hinting)은 우리가 작성한 코드를 다른 개발자가 읽기 수월하게 해주고, 우리가 사용하는 개발 도구가 활용할 수 있도록 도와줄 뿐입니다.
여기서 한 걸음 더 나아가, 더 적극적으로 타입 검사를 하여 코드 품질을 향상시키기 위한 타입 체킹(type checking)에 대해서는 별도의 포스트에서 다루도록 하겠습니다.

변수 타입 어노테이션

먼저 매우 간단한 변수에 타입 어노테이션을 추가하는 방법에 대해서 알아보겠습니다.
변수 이름 뒤에 콜론(:)을 붙이고 타입을 명시해주면 됩니다.

name: str = "John Doe"

age: int = 25

emails: list: ["john1@doe.com", "john2@doe.com"]

address: dict = {
  "street": "54560 Daugherty Brooks Suite 581",
  "city": "Stokesmouth",
  "state": "NM",
  "zip": "80556"
}

함수 타입 어노테이션

함수에 타입 힌탕을 적용할 때는 인자 타입과 반환 타입, 이렇게 두 곳에 추가해줄 수 있습니다.

인자에 타입 어노테이션을 추가할 때는 변수와 동일한 문법을 사용하며, 반환값에 대한 타입을 추가할 때는 화살표(->)를 사용합니다.

def 함수명(<필수 인자>: <인자 타입>, <선택 인자>: <인자 타입> = <기본값>) -> <반환 타입>:
    ...

예를 들면,

def stringify(num: int) -> str:
    return str(num)

def plus(num1: int, num2: float = 3.5) -> float:
    return num1 + num2

def greet(name: str) -> None:
    return "Hi! " + str

def repeat(message: str, times: int = 2) -> list:
    return [message] * times

참고로 콜론(:)과 화살표(->)를 사용할 때는 파이썬의 관행을 따라 콜론은 뒤에만 한 칸을 뛰우고, 화살표는 앞뒤로 한 칸을 띄웁니다.

typing 모듈

내장 타입을 이용해서 좀 더 복잡한 타입 어노테이션을 추가할 때는 스탠다드 라이브러리의 typing 모듈을 사용할 수 있습니다.

from typing import List, Set, Dict, Tuple

nums: List[int] = [1]

unique_nums: Set[int] = {6, 7}

vision: Dict[str, float] = {'left': 1.0, 'right': 0.9}

john: Tuple[int, str, List[float]] = (25, "John Doe", [1.0, 0.9])

typing 내장 모듈에 대한 자세한 설명은 관련 포스트를 참고 바랍니다.

사용자 정의 타입 힌팅

내장 타입 뿐만 아니라 사용자 클래스에 대한 타입 어노테이션도 동일한 방법으로 추가할 수 있습니다.

class User:
    ...

def find_user(id: str) -> User:
    ...

def create_user(user: User) -> User:
    ...

타입 어노테이션 검사

작성한 타입 어노테이션을 검사하고 싶을 때는 __annotations__ 내장 사전 객체를 사용하면 됩니다.

>>> __annotations__
{'no': <class 'int'>, 'name': <class 'str'>, 'age': <class 'int'>, 'address': <class 'dict'>}
>>> repeat.__annotations__
{'message': <class 'str'>, 'times': <class 'int'>, 'return': <class 'list'>}

마치면서

지금까지 파이썬에서 타입 힌팅(type hinting)의 개념과 파이썬 코드에 타입 어노테이션(type annotation)을 어떻게 추가하는지 알아보았습니다.

타입 힌팅은 비강제적이고 선택적으로 적용할 수 있기 때문에 모든 프로젝트에서 부담없이 점진적으로 적용할 수 있습니다.
따라서 신규 프로젝트 뿐만 아니라 기존 프로젝트에서도 코드 가독성과 개발 도구의 효율성을 위해서 고려해보셨으면 좋겠습니다.

다음 포스트에서는 실질적인 코드 품질에 도움이 되는 파이썬의 타입 체킹(type checking)에 대해서 다뤄보도록 하겠습니다.






Engineering Blog  by Dale Seo