
gRPC에 대해서 알아보던중 pyi 파일을 처음 보았고, 이 파일이 Python Type System 중 하나인 stub file 이라는 것을 알게되었습니다. stub file 이 무엇인지 궁금해졌고, 이 궁금증은 Python의 Type System 전체로 확장되었습니다. 전반적인 내용을 정리해보고자 합니다. Python 공식문서중 TypeHint Guide에서 많은 내용을 참고했습니다.
왜 Type System이 필요한가?
우선 프로그래밍 언어를 구분할때 다음과 같은 기준이 있습니다.
- 타입 캐스팅이 자유로운지에 따라 강 타입/약 타입
- 컴파일시 타입을 체크하는지에 따라 정적 타입/동적 타입
이러한 기준에서 봤을때 Python은 약 타입이자, 동적 타이핑 언어입니다. 즉 명시적인 타입 캐스팅 없이 타입 변환이 가능하며, JIT 컴파일을 하긴 하지만 기본적으로 Interpreter 를 통해 동작하기 때문에 런타임에 타입을 체크하는 동적 타입 언어인 것입니다.
이러한 Python에게 Type System 이 필요한 이유는 다음의 이점이 있기 때문입니다.
1. 안정성과 버그 조기 발견:
동적 타이핑은 실행 시점에서 타입 에러를 발견하기 때문에, 큰 규모의 프로젝트나 복잡한 코드에서는 예기치 않은 버그가 발생할 위험이 큽니다. 정적 타입 힌트를 도입하면 코드 작성 시점에서 오류를 미리 잡아내어 안정성을 높일 수 있습니다.
2. 코드 가독성과 문서화:
타입 주석은 함수와 클래스의 의도를 명확하게 해 주며, 이를 통해 코드의 이해도가 향상됩니다. 이는 협업 환경에서 특히 중요한 역할을 합니다.
3. 개발 도구와의 시너지 효과:
타입 힌트를 활용하면 IDE 및 정적 분석 도구(예: mypy, Pyright)와의 연동이 원활해져 자동완성, 리팩토링, 코드 탐색 등에서 생산성을 크게 향상시킬 수 있습니다.
그럼에도 불구하고, Python은 정적 타입 검사를 강제하지 않는다.
정적 타입 힌트가 제공하는 여러 이점에도 불구하고, Python은 여전히 약 타입(dynamic typing) 언어로 남아있습니다. Python 공식 문서에서는 다양한 이유를 들고 있지만 짧게 정리하자면 다음과 같습니다:
Python은 처음부터 동적 타이핑 언어였고, 이 언어를 사용하는 개발자에게 정적 타입 검사를 강제할 순 없다. 타입 어노테이션자체가 가독성을 해치는 경우도 존재하며, 동적 프레임워크의 특성상 정적 타입을 지정하기 어려울 수 있다. 또한 Hyrum's Law에 따르면 타입 어노테이션은 복잡해지거나 Any 로 치환될 가능성이 있다.
Type System How to
이제는 왜 Type System이 필요한지, 그럼에도 불구하고 왜 정적 타입을 강제하지 않는지 알게되었습니다. 그렇다면 Python에서는 Type System을 이용하는 방법을 알아보겠습니다.
간단한 Typing
Python의 typing 모듈을 이용하면 함수 인자, 반환값 등 코드의 주요 부분에 타입 정보를 추가할 수 있습니다. 이는 코드의 의도를 명확히 하고, 정적 분석 도구(예: mypy)를 통한 오류 검출에 도움을 줍니다.
def greet(name: str) -> str:
return f"Hello, {name}!"
# 사용 예시
print(greet("Alice"))
- name: str 은 매개변수 name이 문자열임을 명시합니다.
- -> str 은 함수가 문자열을 반환함을 나타냅니다.
Generic Type
Generic Type은 여러 타입에 대해 동일한 로직을 재사용할 수 있게 해줍니다.
TypeVar와 Generic을 사용하여 타입을 일반화함으로써, 보다 유연하면서도 타입 안전한 코드를 작성할 수 있습니다.
from typing import TypeVar, Generic, List
T = TypeVar('T')
class Stack(Generic[T]):
def __init__(self) -> None:
self._items: List[T] = []
def push(self, item: T) -> None:
self._items.append(item)
def pop(self) -> T:
return self._items.pop()
# 정수형 스택 예시
int_stack = Stack[int]()
int_stack.push(10)
print(int_stack.pop())
# 문자열 스택 예시
str_stack = Stack[str]()
str_stack.push("hello")
print(str_stack.pop())
- T = TypeVar('T')를 통해 임의의 타입 변수를 선언합니다.
- Stack(Generic[T]) 클래스는 다양한 타입의 데이터를 저장할 수 있는 스택을 구현합니다.
- 각각의 스택 인스턴스는 타입 인자를 통해 정해진 타입만을 다루게 됩니다.
Stub File
Stub File(.pyi 파일)은 기존 코드와 별도로 타입 정보를 제공하는 파일입니다. 이를 통해 라이브러리나 기존 코드베이스에 직접 손대지 않고도 정적 타입 정보를 추가할 수 있어, 점진적 도입에 매우 유용합니다. 특히 외부 라이브러리(external library)의 경우, 원본 코드를 수정할 수 없거나 수정이 어려운 상황에서 Stub File을 이용하여 타입 정보를 보완할 수 있습니다. 이러한 이점 때문에 Python에서 gRPC를 활용할때 사용됩니다.
mylib.py:
def add(a, b):
return a + b
mylib.pyi:
def add(a: int, b: int) -> int: ...
- 실제 코드(mylib.py)는 타입 정보 없이 동작하지만, 타입 스텁 파일(mylib.pyi)은 정적 타입 검사기에게 함수 add의 인자와 반환값이 모두 정수임을 알려줍니다.
- 외부 라이브러리의 경우, 해당 라이브러리가 타입 정보를 포함하고 있지 않다면, 별도의 Stub File을 제공하여 사용자들이 정적 타입 검사 도구를 통해 타입 안전성을 확보할 수 있도록 돕습니다.
- 예를 들어, typeshed 프로젝트는 Python의 표준 라이브러리와 다양한 외부 라이브러리의 타입 정보를 수집하여 관리하며, 이를 통해 많은 라이브러리들이 정적 타입 검사를 활용할 수 있게 합니다.
참고
Python Document, TypeSystem Guides: https://typing.python.org/en/latest/guides/index.html
Python Docuemnt,Stubfile: https://typing.python.org/en/latest/spec/distributing.html#stub-files
Python Document, Writing and Maintaining: https://typing.python.org/en/latest/guides/writing_stubs.html
pep TypeHints: https://peps.python.org/pep-0484
Python 3.13 JIT: https://docs.python.org/3/whatsnew/3.13.html#an-experimental-just-in-time-jit-compiler
'탐구 생활 > python' 카테고리의 다른 글
Python asyncio에 대한 탐구 (0) | 2025.03.23 |
---|---|
python 사용자 지정 불변 객체를 만드는 3가지 방법 (0) | 2024.11.17 |
Python 가변 객체와 불변 객체 (0) | 2024.11.17 |