typing — 타입 힌트 지원

typing — 타입 힌트 지원

참고: Python 런타임은 함수와 변수 타입 주석(annotation)을 강제하지 않아요. 이것들은 타입 검사기, IDE, 린터 같은 서드파티 도구에서 사용할 수 있어요.

이 모듈은 타입 힌트에 대한 런타임 지원을 제공해요. 버전 3.5에서 추가되었어요.

출처: Python documentation

본문

다음 함수를 생각해보세요:

def surface_area_of_cube(edge_length: float) -> str:
    return f"The surface area of the cube is {6 * edge_length ** 2}."

suraface_area_of_cube 함수는 타입 힌트 edge_length: float가 나타내는 대로 float의 인스턴스가 될 것으로 기대되는 인자를 받아요. -> str 힌트가 나타내는 대로 str 인스턴스를 반환할 것으로 기대돼요. 타입 힌트는 floatstr 같은 간단한 클래스일 수 있지만 더 복잡할 수도 있어요. typing 모듈은 더 고급 타입 힌트의 어휘를 제공해요.

새 기능이 typing 모듈에 자주 추가돼요. typing_extensions 패키지는 이러한 새 기능을 이전 Python 버전에 백포트해 제공해요.

타입 별칭 (Type aliases)

타입 별칭은 TypeAliasType의 인스턴스를 만드는 type 문을 사용해 정의돼요. 이 예에서 Vectorlist[float]는 정적 타입 검사기에 의해 동등하게 처리돼요:

type Vector = list[float]

def scale(scalar: float, vector: Vector) -> Vector:
    return [scalar * num for num in vector]

# 타입 검사 통과; float 목록은 Vector로 자격이 됨
new_vector = scale(2.0, [1.0, -4.2, 5.4])

타입 별칭은 복잡한 타입 시그니처를 단순화하는 데 유용해요. type 문은 Python 3.12에서 새로 나왔어요. 이전 버전과의 호환을 위해 타입 별칭은 단순 할당(Vector = list[float])으로 만들거나 TypeAlias로 표시할 수 있어요.

타입 별칭은 두 타입이 서로 동등함을 선언해요. type Alias = Original은 정적 타입 검사기가 모든 경우에 Alias를 Original과 정확히 동등하게 취급하게 해요.

NewType

NewType 헬퍼로 구별되는 타입을 만들어요:

from typing import NewType

UserId = NewType('UserId', int)
some_id = UserId(524313)

정적 타입 검사기는 새 타입을 원래 타입의 하위 클래스인 것처럼 취급해요. 이는 논리 오류를 잡는 데 유용해요. Derived = NewType('Derived', Base) 문은 Derived를 전달하는 매개변수를 즉시 반환하는 호출 가능하게 만든다. 즉 런타임에 some_value is Derived(some_value)가 항상 true예요. Derived의 하위 타입을 만드는 것은 유효하지 않아요.

반면 NewType은 한 타입을 다른 타입의 하위 타입으로 선언해요. Derived = NewType('Derived', Original)은 정적 타입 검사기가 DerivedOriginal의 하위 클래스로 취급하게 해요. 이는 최소한의 런타임 비용으로 논리 오류를 방지하려 할 때 유용해요.

호출 가능 객체 주석 (Annotating callable objects)

함수 — 또는 다른 호출 가능 객체 — 는 collections.abc.Callable 또는 더 이상 사용되지 않는 typing.Callable을 사용해 주석을 달 수 있어요. Callable[[int], str]int 타입의 단일 매개변수를 받고 str을 반환하는 함수를 나타내요. 구독 구문은 항상 정확히 두 값(인자 목록과 반환 타입)과 함께 사용해야 해요.

리터럴 줄임표 ...가 인자 목록으로 주어지면, 임의의 매개변수 목록을 가진 호출 가능이 허용됨을 나타내요:

def concat(x: str, y: str) -> str:
    return x + y

x: Callable[..., str]
x = str        # OK
x = concat     # OK

Callable은 가변 인자의 함수, 오버로드된 함수, 키워드 전용 매개변수가 있는 함수 같은 복잡한 시그니처를 표현할 수 없어요. 그러나 이러한 시그니처는 __call__() 메서드를 가진 Protocol 클래스를 정의해 표현할 수 있어요. 다른 호출 가능을 인자로 받는 호출 가능은 ParamSpec을 사용해 그 매개변수 타입이 서로 의존함을 나타낼 수 있고, Concatenate 연산자를 사용할 수 있어요.

제네릭 (Generics)

컨테이너에 보관된 객체의 타입 정보는 제네릭 방식으로 정적으로 추론될 수 없으므로, 표준 라이브러리의 많은 컨테이너 클래스는 컨테이너 요소의 예상 타입을 나타내는 구독을 지원해요:

from collections.abc import Mapping, Sequence

class Employee: ...

def notify_by_email(employees: Sequence[Employee], overrides: Mapping[str, str]) -> None: ...

제네릭 함수와 클래스는 타입 매개변수 구문으로 매개변수화할 수 있어요:

from collections.abc import Sequence

def first[T](l: Sequence[T]) -> T:  # 함수는 TypeVar "T"에 대해 제네릭
    return l[0]

또는 TypeVar 팩토리를 직접 사용해요:

from typing import TypeVar
U = TypeVar('U')  # 타입 변수 "U" 선언

def second(l: Sequence[U]) -> U:  # 함수는 TypeVar "U"에 대해 제네릭
    return l[1]

튜플 주석 (Annotating tuples)

Python의 타입 시스템은 다른 대부분의 컨테이너에서 모든 요소가 같은 타입이라고 가정해요. 그러나 관용적인 Python 코드에서 튜플은 요소가 모두 같은 타입이 아닌 것이 흔해요. 이 때문에 tuple은 Python의 타입 시스템에서 특별히 처리돼요. tuple은 임의의 수의 타입 인자를 받아요.

어떤 길이일 수 있고 모든 요소가 같은 타입 T인 튜플을 나타내려면 리터럴 줄임표 ...를 사용해요: tuple[T, ...]. 빈 튜플은 tuple[()]을 사용해요. 일반 tuple을 주석으로 사용하는 것은 tuple[Any, ...]를 사용하는 것과 동등해요.

클래스 객체의 타입 (The type of class objects)

C로 주석을 단 변수는 C 타입의 값을 받을 수 있어요. 반면 type[C]로 주석을 단 변수는 클래스 그 자체인 값을 받아요. type[C]는 공변(covariant)이에요. type의 유일한 합법적 매개변수는 클래스, Any, 타입 변수, 그리고 이들의 유니언이에요. type[Any]는 Python의 메타클래스 계층의 루트인 type과 동등해요.

제너레이터와 코루틴 주석 (Annotating generators and coroutines)

제너레이터는 제네릭 타입 Generator[YieldType, SendType, ReturnType]으로 주석을 달 수 있어요. 표준 라이브러리의 다른 많은 제네릭 클래스와 달리 GeneratorSendType은 공변이 아니라 반공변(contravariant)으로 동작해요. SendTypeReturnType 매개변수는 기본값이 None이에요.

코루틴은 Coroutine[YieldType, SendType, ReturnType]으로 주석을 달 수 있어요.

유니언 (Unions)

두 개 이상의 타입의 유니언은 | 연산자(int | str)나 typing.Union을 사용해 표현할 수 있어요. 다음 두 문은 서로 동일해요:

int | str
Union[int, str]

기타 특수 형태 (Special forms)

  • Any — 제한 없는 타입. Any 타입의 값은 모든 타입과 호환되고, 모든 타입은 Any와 호환돼요.
  • Literal — 특정 리터럴 값만 허용하는 타입을 나타내요.
  • Optional[X]X | None과 동등.
  • TypeVar — 타입 변수.
  • Protocol — 구조적 서브타이핑을 위한 프로토콜.
  • TypedDict — dict의 타입 힌트.
  • NamedTuple — namedtuple의 타입 힌트.
  • overload — 오버로드된 함수 정의.
  • Final — 최종 타입 지정.
  • ClassVar — 클래스 변수를 나타내요.
  • Self — 메서드의 반환 타입이 클래스 인스턴스 자신임을 나타내요.

기타 주제

  • Constant typesFinalClassVar
  • Annotating tuples — 다양한 길이의 튜플
  • User-defined generic types — 사용자 정의 제네릭 타입
  • The type of class objectstype[C]
  • Annotating generators and coroutinesGenerator, Coroutine
  • Type aliasestype 문, TypeAlias
  • NewType — 구별되는 타입
  • Callback protocols__call__() 메서드를 가진 Protocol
  • Type parameter syntax — PEP 695 타입 매개변수 구문

typing 모듈의 타입 시스템은 PEP를 통해 표준화되어 있어요. 자세한 내용은 "Specification for the Python Type System"을 참고하세요.

더 알아보기 (Learn more)