Following system colour scheme Selected dark colour scheme Selected light colour scheme

Python 개선 제안 한국어 번역

PEP 746 – Annotated 메타데이터 타입 검사

Author:
Adrian Garcia Badaracco <adrian at adriangb.com>
Sponsor:
Jelle Zijlstra <jelle.zijlstra at gmail.com>
Discussions-To:
Discourse thread
Status:
Draft
Type:
Standards Track
Topic:
Typing
Created:
20-May-2024
Python-Version:
3.15
Post-History:
20-May-2024

Table of Contents

번역·라이선스 안내

이 비공식 한국어 번역은 원문 Copyright 절의 Public Domain 조건에 따라 제공합니다. 원저자와 공식 원문은 그대로 표시합니다. 수정되지 않은 기준 원문 · 공식 최신판

초록

이 PEP는 typing.Annotated 타입을 사용하는 메타데이터를 타입 검사하는 메커니즘을 제안합니다. 새로운 __supports_annotated_base__ 프로토콜을 구현하는 메타데이터 객체는 정적 타입 검사기가 타입 검사하며, 이를 통해 메타데이터가 주어진 타입에 유효한지 확인합니다.

동기

PEP 593은 타입에 런타임 메타데이터를 붙이는 방법으로 Annotated를 도입했습니다. 일반적으로 이 메타데이터는 정적 타입 검사기를 위한 것이 아니지만, 그렇더라도 메타데이터가 주어진 타입에 타당한지 검사할 수 있으면 유용한 경우가 많습니다.

Annotated를 사용하여 필드에 직렬화 정보를 붙이는 PEP 593의 첫 번째 예제를 살펴봅시다:

class Student(struct2.Packed):
    name: Annotated[str, struct2.ctype("<10s")]

여기서 struct2.ctype("<10s") 메타데이터는 직렬화 라이브러리가 필드를 직렬화하는 데 사용하기 위한 것입니다. 이러한 라이브러리는 일부 타입만 직렬화할 수 있으므로, 예를 들어 Annotated[list[str], struct2.ctype("<10s")]처럼 작성하는 것은 의미가 없습니다. 그러나 타입 시스템은 이를 강제할 방법을 제공하지 않습니다. 타입 검사기는 메타데이터를 완전히 무시합니다.

이러한 사용 사례는 Annotated를 사용하여 필드에 검증 및 변환 정보를 붙이는 pydantic과 msgspec 같은 라이브러리나, Annotated를 사용하여 매개변수가 헤더, 쿼리 문자열 또는 의존성 주입에서 추출된 것임을 표시하는 fastapi 같은 라이브러리에서 나타납니다.

명세

이 PEP는 정적 및 런타임 타입 검사기가 Annotated 메타데이터와 주어진 타입 사이의 일관성을 검증하는 데 사용할 수 있는 프로토콜을 도입합니다. 이 프로토콜을 구현하는 객체에는 메타데이터가 주어진 타입에 유효한지를 지정하는 __supports_annotated_base__라는 속성이 있습니다:

class Int64:
    __supports_annotated_base__: int

데이터클래스와의 상호 작용을 피하기 위해 이 속성을 ClassVar로 표시할 수도 있습니다:

from dataclasses import dataclass
from typing import ClassVar

@dataclass
class Gt:
    value: int
    __supports_annotated_base__: ClassVar[int]

정적 타입 검사기가 Annotated[T, M1, M2, ...] 형태의 타입 표현식을 만나면, M1, M2, ...의 각 메타데이터 요소에 대해 다음 중 하나가 성립하도록 강제해야 합니다:

  • 메타데이터 요소가 __supports_annotated_base__ 속성이 없는 객체로 평가되거나,
  • 메타데이터 요소가 __supports_annotated_base__ 속성을 가진 객체 M으로 평가되고, T를 M.__supports_annotated_base__의 타입에 할당할 수 있습니다.

제네릭 Gt 메타데이터를 지원하려면 다음과 같이 작성할 수 있습니다:

from typing import Protocol

class SupportsGt[T](Protocol):
    def __gt__(self, __other: T) -> bool:
        ...

class Gt[T]:
    __supports_annotated_base__: ClassVar[SupportsGt[T]]

    def __init__(self, value: T) -> None:
        self.value = value

x1: Annotated[int, Gt(0)] = 1  # OK
x2: Annotated[str, Gt(0)] = 0  # type checker error: str is not assignable to SupportsGt[int]
x3: Annotated[int, Gt(1)] = 0  # OK for static type checkers; runtime type checkers may flag this

하위 호환성

프로토콜을 구현하지 않는 메타데이터는 모든 타입에 대해 유효한 것으로 간주되므로, 기존 코드에 호환성을 깨뜨리는 변경은 생기지 않습니다. 새로운 검사는 이 PEP에서 명시한 프로토콜을 명시적으로 구현하는 메타데이터 객체에만 적용됩니다.

보안 영향

없습니다.

교육 방법

이 프로토콜은 주로 Annotated 메타데이터를 제공하는 라이브러리를 위한 것이며, 해당 라이브러리의 최종 사용자가 직접 프로토콜을 구현해야 할 가능성은 낮습니다. 이 프로토콜은 typing.Annotated 문서와 타이핑 명세에서 언급되어야 합니다.

참조 구현

아직 없습니다.

기각된 아이디어

제네릭 클래스 대신 타입 변수 도입

Annotated 내부 타입의 타입 T를 나타내기 위해 특수한 타입 변수 AnnotatedT = TypeVar("AnnotatedT")를 사용하는 방안을 검토했습니다. 이 경우 메타데이터는 이 타입 변수를 기준으로 타입 검사를 받게 됩니다. 그러나 이 방식을 쓰려면 (PEP 695 이전의) 기존 타입 변수 문법을 사용해야 하며, 이 문법은 현재 권장되지 않는 기능입니다. 또한 이 방식은 타입 시스템의 나머지 부분과 잘 맞지 않는 특이한 방식으로 타입 변수를 사용하게 됩니다.

모든 메타데이터 객체가 서브클래스로 상속해야 하는 새 타입을 typing.py에 도입하기

이 PEP의 이전 버전에서는 메타데이터 객체가 서브클래스로 상속하게 될 새로운 제네릭 베이스 클래스 TypedMetadata[U]를 추가하자고 제안했습니다. 메타데이터 객체가 TypedMetadata[U]의 서브클래스라면, 타입 검사기는 어노테이션의 기반 타입이 U에 할당 가능한지 검사합니다. 그러나 이 메커니즘은 언어의 나머지 부분과 잘 통합되지 않습니다. Python은 일반적으로 마커 베이스 클래스를 사용하지 않습니다. 또한 이 방식은 현재 제안보다 유연성이 떨어집니다. 오버로드를 허용하지 않으며, 메타데이터 객체가 새로운 베이스 클래스를 추가해야 하므로 런타임 구현이 더 복잡해질 수 있습니다.

__supports_annotated_base__에 속성 대신 메서드 사용

프로토콜에 속성 대신 메서드를 사용하여, 런타임에 이 메서드로 메타데이터의 유효성을 검사하고 오버로드나 불리언 리터럴 반환을 지원할 수 있도록 하는 방안을 검토했습니다. 그러나 메서드를 사용하면 구현에 보일러플레이트가 추가되며, 런타임 사용 사례나 오버로드 및 불리언 리터럴 반환이 관련된 더 복잡한 시나리오의 가치가 분명하지 않았습니다.

감사의 글

프로토콜을 사용하는 아이디어를 제안하고 Pyright에 임시 지원을 구현해 주신 Eric Traut에게 감사드립니다. 이 PEP를 후원해 주신 Jelle Zijlstra에게 감사드립니다.