PEP 613 – 명시적 타입 별칭
- Author:
- Shannon Zhu <szhu at fb.com>
- Sponsor:
- Guido van Rossum <guido at python.org>
- Discussions-To:
- Typing-SIG thread
- Status:
- Final
- Type:
- Standards Track
- Topic:
- Typing
- Created:
- 21-Jan-2020
- Python-Version:
- 3.10
- Post-History:
- 21-Jan-2020
번역·라이선스 안내
이 비공식 한국어 번역은 원문 Copyright 절의 Public Domain or CC0-1.0, whichever is more permissive 조건에 따라 제공합니다. 원저자와 공식 원문은 그대로 표시합니다. 수정되지 않은 기준 원문 · 공식 최신판
개요
타입 별칭은 모든 타입 힌트만큼 복잡할 수 있는 사용자가 지정한 타입이며, 모듈 최상위 수준에서 간단한 변수 할당으로 지정합니다.
이 PEP는 할당을 타입 별칭으로 명시적으로 선언하는 방법을 공식화합니다.
동기
타입 별칭은 최상위 변수 할당으로 선언합니다. 유효한 타입 별칭과 전역 변수의 구분은 PEP 484에서 암묵적으로 결정되었습니다. top level 대입에 어노테이션이 없고 대입된 값이 유효한 타입이면, 대입되는 이름은 유효한 타입 별칭입니다. 그렇지 않으면 해당 이름은 타입 힌트로 사용할 수 없는 단순한 전역 값입니다.
이러한 암묵적 타입 별칭 선언 규칙은 타입 별칭에 전방 참조나 유효하지 않은 타입이 포함되거나, 타입 별칭 선언에 적용되는 다른 제한을 위반할 때 혼란을 일으킵니다. 어노테이션이 없는 값과 타입 별칭의 구분이 암묵적이므로, 모호하거나 잘못된 타입 별칭 선언은 암묵적으로 유효한 값 할당으로 처리됩니다. 이로 인해 타입 별칭으로 표현할 수 없는 표현식이 생기며, 잘못 구성된 타입 별칭의 오류 진단이 이후 단계로 미뤄집니다.
다음 예제에는 각각 기존의 암묵적 별칭 선언에서 비롯되는 최적이 아니거나 혼란스러운 동작의 예가 포함되어 있습니다. 또한 비교를 위해 여기에서 TypeName: TypeAlias = Expression 형식의 명시적 별칭도 소개하지만, 구문은 이후 섹션에서 더 자세히 설명합니다.
전방 참조:
MyType = "ClassName"
def foo() -> MyType: ...
나중에 ClassName이 정의되기만 한다면 이 코드 조각은 오류를 발생시키지 않아야 합니다. 그러나 타입 검사기는 MyType이 타입 별칭이 아니라 값 할당이라고 가정할 수밖에 없으므로, (1) MyType이 어노테이션이 없는 전역 문자열이며 (2) MyType이 유효한 타입이 아니므로 반환 어노테이션으로 사용할 수 없다는 잘못된 오류를 발생시킬 수 있습니다.
MyType: TypeAlias = "ClassName"
def foo() -> MyType: ...
명시적 별칭을 사용하면 모호성이 제거되므로 위의 두 오류가 모두 발생하지 않습니다. 또한 ClassName에 문제가 있는 경우(즉, 실제로 나중에 정의되지 않는 경우) 타입 검사기가 오류를 발생시킬 수 있습니다.
오류 메시지:
MyType1 = InvalidType
MyType2 = MyGeneric(int) # i.e., intention was MyGeneric[int]
타입 검사기는 이 코드 조각에서 InvalidType이 유효한 타입이 아니므로 표현식에 어노테이션을 지정하거나 타입 별칭을 구성하는 데 사용할 수 없다고 경고해야 합니다. 대신 타입 검사기는 (1) MyType이 어노테이션이 누락된 전역 표현식이며 (2) 코드베이스 전반에서 MyType을 사용하는 모든 경우에 MyType이 유효한 타입이 아니라는 잘못된 오류를 발생시킬 수밖에 없습니다.
MyType1: TypeAlias = InvalidType
MyType2: TypeAlias = MyGeneric(int)
명시적 별칭을 사용하면 타입 검사기에 잘못된 타입 별칭의 실제 정의에서 오류를 보고하고 그 이유를 설명할 충분한 정보가 있습니다. 즉, MyGeneric(int) 및 InvalidType이 유효한 타입이 아니기 때문입니다. 값 표현식이 더 이상 전역 값으로 평가되지 않으면 코드베이스 전반에서 MyType을 사용하는 모든 경우의 조치할 수 없는 타입 오류를 억제할 수 있습니다.
범위 제한:
class Foo:
x = ClassName
y: TypeAlias = ClassName
z: Type[ClassName] = ClassName
타입 별칭은 클래스 범위 내에서 암묵적(x)으로든 명시적(y)으로든 유효합니다. 해당 줄을 클래스 변수로 해석해야 한다면 명시적으로 어노테이션을 지정해야 합니다(z).
x = ClassName
def foo() -> None:
x = ClassName
바깥쪽 x는 유효한 타입 별칭이지만, 타입 별칭은 함수 내부에서 정의할 수 없으므로 안쪽 x가 타입으로 사용되는 경우 타입 검사기는 오류를 발생시켜야 합니다. 별칭 선언 규칙이 명시적이지 않고, 타입 오류가 안쪽 타입 별칭 선언 위치에서 발생하는 것이 아니라 그 이후의 모든 사용 사례에서 발생하기 때문에 혼란스럽습니다.
x: TypeAlias = ClassName
def foo() -> None:
x = ClassName
def bar() -> None:
x: TypeAlias = ClassName
명시적 별칭을 사용하면 외부 할당은 여전히 유효한 타입 변수입니다. foo내부에서 내부 할당은 x: Type[ClassName]으로 해석되어야 합니다. bar내부에서 타입 검사기는 함수 내부에서 타입 별칭을 정의할 수 없다는 것을 작성자에게 알리는 명확한 오류를 발생시켜야 합니다.
명세
명시적 별칭 선언 구문은 가능한 세 가지 종류의 할당, 즉 타입이 지정된 전역 표현식, 타입이 지정되지 않은 전역 표현식, 타입 별칭을 명확하게 구분합니다. 이는 어노테이션이 추가될 때 타입 검사를 깨뜨리는 할당이 존재하는 것을 방지하고, 값의 타입에 기반하여 할당의 성격을 분류하는 것을 방지합니다.
암묵적 구문(기존):
x = 1 # untyped global expression
x: int = 1 # typed global expression
x = int # type alias
x: Type[int] = int # typed global expression
명시적 구문:
x = 1 # untyped global expression
x: int = 1 # typed global expression
x = int # untyped global expression (see note below)
x: Type[int] = int # typed global expression
x: TypeAlias = int # type alias
x: TypeAlias = "MyClass" # type alias
참고: 위의 예제는 암묵적 별칭 선언과 명시적 별칭 선언을 각각 독립적으로 보여줍니다. 하위 호환성을 위해 타입 검사기는 두 가지를 동시에 지원해야 하며, 이는 타입이 지정되지 않은 전역 표현식 x = int가 여전히 유효한 타입 별칭으로 간주됨을 의미합니다.
하위 호환성
명시적 별칭은 타입 별칭을 선언하는 대체 방법을 제공하지만, 기존의 모든 코드와 이전 별칭 선언은 이전과 같이 작동합니다.
참조 구현
Pyre 타입 검사기는 명시적 타입 별칭 선언을 지원합니다.
거부된 아이디어
명시적 별칭에 대해 몇 가지 대체 구문이 검토되었습니다.
MyType: TypeAlias[int]
이는 초기화되지 않은 변수와 매우 비슷해 보입니다.
MyType = TypeAlias[int]
위 옵션과 마찬가지로, 이 형식도 MyType의 런타임 값이 무엇인지에 대한 혼란을 더할 수 있습니다.
이에 비해, 선택된 구문 옵션인 MyType: TypeAlias = int는 여전히 MyType = int 할당 구문을 따르면서, 순수하게 어노테이션으로서 타입 검사기에 몇 가지 정보를 추가한다는 점에서 매력적입니다.
버전 이력
- 2021-11-16
- 클래스 스코프 내에서
TypeAlias를 허용
- 클래스 스코프 내에서
Copyright
This document is placed in the public domain or under the CC0-1.0-Universal license, whichever is more permissive.