| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | |||
| 5 | 6 | 7 | 8 | 9 | 10 | 11 |
| 12 | 13 | 14 | 15 | 16 | 17 | 18 |
| 19 | 20 | 21 | 22 | 23 | 24 | 25 |
| 26 | 27 | 28 | 29 | 30 |
- #object detection
- async
- #activation function
- 멀티 스레드
- 비동기
- multi-thread
- #pytorch
- #Jetson nano
- #Linux
- asyncio
- #annotation
- Linux
- #Anaconda
- #landmark
- #torchvision
- pytorch
- #비선형
- #선형
- #gpu training
- Today
- Total
Wooks_learning
[Python] typing — Optional, Union, List, Dict, Tuple, Set 본문
[Python] typing — Optional, Union, List, Dict, Tuple, Set
Wooks_ 2026. 3. 24. 18:42타입 힌트를 쓰면 코드가 명확해지고, 실수를 줄일 수 있다.
하지만 typing 모듈을 처음 접하면 언제 뭘 써야 할지 헷갈린다.
이 글에서 자주 쓰이는 핵심 6가지를 예제 중심으로 정리한다.
typing이란?
Python은 기본적으로 동적 타입 언어다. 변수에 어떤 타입의 값을 넣어도 런타임에 오류가 나지 않는다.
x = 10
x = "hello" # 오류 없음
하지만 코드가 커질수록 이 유연함이 오히려 독이 된다.
"이 함수가 뭘 받고 뭘 반환하는지"를 코드만 보고 파악하기 어려워지기 때문이다.
typing 모듈은 이 문제를 해결하기 위해 Python 3.5에서 도입됐다.
타입을 강제하지는 않지만, 힌트를 명시함으로써 가독성을 높이고 정적 분석 도구(mypy, pylance 등)와 IDE의 자동완성을 지원한다.
중요: 타입 힌트는 런타임에 강제되지 않는다.
잘못된 타입을 넘겨도 Python은 오류를 내지 않는다.
단, mypy 같은 정적 분석 도구를 쓰면 실행 전에 타입 오류를 잡을 수 있다.
1. Optional — None이 될 수 있는 값
개념
Optional[X]는 "X 타입이거나 None일 수 있다" 는 의미다.
값이 존재하지 않을 수도 있는 상황에 사용한다.
from typing import Optional
Optional[str] # str 또는 None
내부적으로는 Union[X, None]과 완전히 동일하다.
예제
from typing import Optional
# 유저를 찾으면 이름을 반환하고, 없으면 None을 반환
def find_user(user_id: int) -> Optional[str]:
if user_id == 1:
return "Alice"
return None
# 인자의 기본값이 None인 경우
def connect(host: str, port: Optional[int] = None) -> None:
if port is None:
port = 8080
print(f"Connecting to {host}:{port}")
주의
Optional을 쓴다고 해서 None 처리를 자동으로 해주지는 않는다.
반드시 코드 안에서 None 여부를 직접 확인해야 한다.
def greet(name: Optional[str]) -> str:
if name is None: # None 체크 필수
return "Hello, stranger!"
return f"Hello, {name}!"
Python 3.10+ 대안
# 아래 두 표현은 동일하다
def find_user(user_id: int) -> Optional[str]: ...
def find_user(user_id: int) -> str | None: ... # 3.10+ 권장
2. Union — 여러 타입 중 하나
개념
Union[X, Y]는 "X 또는 Y 타입" 을 의미한다.
두 가지 이상의 타입을 허용해야 할 때 사용한다.
from typing import Union
Union[int, str] # int 또는 str
Union[int, str, None] # int 또는 str 또는 None (= Optional[Union[int, str]])
예제
from typing import Union
# 숫자 또는 문자열을 받아서 처리
def process(value: Union[int, str]) -> str:
if isinstance(value, int):
return f"숫자: {value * 2}"
return f"문자열: {value.upper()}"
print(process(10)) # 숫자: 20
print(process("hello")) # 문자열: HELLO
Optional과의 관계
# 아래 두 표현은 완전히 동일하다
Optional[str]
Union[str, None]
Optional은 Union[X, None]의 축약 표현일 뿐이다.
Python 3.10+ 대안
# 아래 두 표현은 동일하다
def process(value: Union[int, str]) -> str: ...
def process(value: int | str) -> str: ... # 3.10+ 권장
3. List — 리스트의 원소 타입 명시
개념
List[X]는 "X 타입 원소들로 이루어진 리스트" 를 의미한다.
from typing import List
List[int] # 정수 리스트
List[str] # 문자열 리스트
예제
from typing import List
def get_scores(students: List[str]) -> List[int]:
score_map = {"Alice": 90, "Bob": 85, "Charlie": 92}
return [score_map.get(s, 0) for s in students]
scores = get_scores(["Alice", "Bob"])
print(scores) # [90, 85]
중첩 사용
from typing import List
# 리스트 안에 리스트
matrix: List[List[int]] = [
[1, 2, 3],
[4, 5, 6],
]
Python 3.9+ 대안
# 아래 두 표현은 동일하다
def foo(items: List[int]) -> None: ...
def foo(items: list[int]) -> None: ... # 3.9+ 부터 소문자 list 사용 가능
4. Dict — 딕셔너리의 키/값 타입 명시
개념
Dict[K, V]는 "K 타입의 키와 V 타입의 값으로 이루어진 딕셔너리" 를 의미한다.
from typing import Dict
Dict[str, int] # 문자열 키, 정수 값
Dict[int, str] # 정수 키, 문자열 값
예제
from typing import Dict, List
def count_words(words: List[str]) -> Dict[str, int]:
result: Dict[str, int] = {}
for word in words:
result[word] = result.get(word, 0) + 1
return result
print(count_words(["apple", "banana", "apple"]))
# {'apple': 2, 'banana': 1}
중첩 사용
from typing import Dict, List
# 유저별 점수 목록
user_scores: Dict[str, List[int]] = {
"Alice": [90, 85, 92],
"Bob": [78, 88, 95],
}
Python 3.9+ 대안
# 아래 두 표현은 동일하다
def foo(data: Dict[str, int]) -> None: ...
def foo(data: dict[str, int]) -> None: ... # 3.9+ 부터 소문자 dict 사용 가능
5. Tuple — 고정 길이, 고정 타입의 순서쌍
개념
Tuple[X, Y, Z]는 "첫 번째 원소는 X, 두 번째는 Y, 세 번째는 Z 타입인 튜플" 을 의미한다.
List와 달리 길이와 각 위치의 타입이 고정된다.
from typing import Tuple
Tuple[str, int] # (이름, 나이) 같은 구조
Tuple[int, int, int] # (x, y, z) 좌표 같은 구조
예제
from typing import Tuple
# 이름과 나이를 함께 반환
def get_user_info(user_id: int) -> Tuple[str, int]:
return ("Alice", 30)
name, age = get_user_info(1)
print(f"{name}은 {age}세입니다.") # Alice은 30세입니다.
가변 길이 Tuple
길이가 정해지지 않은 같은 타입의 튜플은 ...을 사용한다.
from typing import Tuple
# 정수만 담긴 튜플 (길이 무관)
def sum_all(values: Tuple[int, ...]) -> int:
return sum(values)
print(sum_all((1, 2, 3, 4, 5))) # 15
Python 3.9+ 대안
# 아래 두 표현은 동일하다
def foo() -> Tuple[str, int]: ...
def foo() -> tuple[str, int]: ... # 3.9+ 부터 소문자 tuple 사용 가능
6. Set — 집합의 원소 타입 명시
개념
Set[X]는 "X 타입의 원소들로 이루어진 집합" 을 의미한다.
List와 비슷하지만 중복을 허용하지 않고 순서가 없다는 점이 다르다.
from typing import Set
Set[int] # 정수 집합
Set[str] # 문자열 집합
예제
from typing import Set, List
# 중복 제거 후 고유한 태그 목록 반환
def get_unique_tags(posts: List[List[str]]) -> Set[str]:
result: Set[str] = set()
for tags in posts:
result.update(tags)
return result
tags = get_unique_tags([
["python", "backend"],
["python", "typing"],
["backend", "api"],
])
print(tags) # {'python', 'backend', 'typing', 'api'}
Python 3.9+ 대안
# 아래 두 표현은 동일하다
def foo(items: Set[str]) -> None: ...
def foo(items: set[str]) -> None: ... # 3.9+ 부터 소문자 set 사용 가능
전체 요약
타입 의미 예시 3.9+ 대안
| Optional[X] | X 또는 None | Optional[str] | str | None |
| Union[X, Y] | X 또는 Y | Union[int, str] | int | str |
| List[X] | X 타입의 리스트 | List[int] | list[int] |
| Dict[K, V] | K키 V값 딕셔너리 | Dict[str, int] | dict[str, int] |
| Tuple[X, Y] | 고정 길이 순서쌍 | Tuple[str, int] | tuple[str, int] |
| Set[X] | X 타입의 집합 | Set[str] | set[str] |
버전별 권장 사용법 정리
# Python 3.8 이하 — typing 모듈 필수
from typing import List, Dict, Tuple, Set, Optional, Union
def foo(
names: List[str],
scores: Dict[str, int],
point: Tuple[int, int],
tags: Set[str],
nickname: Optional[str],
value: Union[int, str],
) -> None:
...
# Python 3.9 이상 — 소문자 내장 타입 사용 가능 (List, Dict 등 불필요)
# Python 3.10 이상 — | 연산자로 Union, Optional 대체 가능
def foo(
names: list[str],
scores: dict[str, int],
point: tuple[int, int],
tags: set[str],
nickname: str | None,
value: int | str,
) -> None:
...
마치며
타입 힌트는 처음에는 "그냥 주석 아닌가?" 싶을 수 있다.
하지만 코드가 커지고 협업이 시작되면, 타입 힌트가 있고 없고의 차이가 크게 느껴진다.
특히 Optional로 None 가능성을 명시하거나, Dict[str, List[int]]처럼 중첩 구조를 표현하면 함수의 동작을 문서 없이도 바로 파악할 수 있다.
당장 모든 코드에 적용하기 어렵다면, 함수의 인자와 반환값부터 타입 힌트를 붙이는 습관을 들여보자.
참고
'Computer science > Python' 카테고리의 다른 글
| [Python] Call by value와 call by reference (0) | 2024.12.05 |
|---|