728x90

Python에서 타입 힌트 등장 배경


Python은 동적 타입 언어로 알려져 있다. 이는 프로그래머가 변수의 타입을 미리 선언하지 않아도 되며, 프로그램 실행 시점에서 타입이 결정된다. 이러한 동적 타이핑은 개발의 유연성을 높이지만, 대규모 프로젝트나 복잡한 시스템에서는 종종 문제를 야기한다. 타입 오류가 런타임에서만 발견되기 때문에, 버그를 예방하고, 코드의 안정성을 향상시키기 어렵다. 이러한 문제점을 해결하기 위해 Python 3.5 버전에서 typing 모듈이 도입되었습니다.

 

타입 힌트(typing Module)


Python의 typing 모듈은 개발자들이 변수, 함수 매개변수, 반환값 등의 데이터 타입을 명시적으로 선언할 수 있게 해주는 기능을 제공한다. 주로 코드의 가독성을 향상시키고, 정적 분석 도구를 사용한 오류 검출, 그리고 개발 환경에서의 향상된 자동 완성 및 코드 분석을 위해 사용한다. 

주요 기능은 다음과 같다.

  • 기본 데이터 타입: int, float, str, bool 등 Python의 기본 데이터 타입.
  • 컨테이너 타입: List[T], Dict[K, V], Set[T], Tuple[T, ...] 등과 같이 요소의 타입을 명시할 수 있는 컨테이너.
  • 복합 타입: Union[T1, T2]은 T1 또는 T2 타입의 값이 될 수 있음을 나타내고, Optional[T]은 None이 될 수 있는 T 타입을 의미.
  • 특수 타입: Any는 모든 타입을 허용하며, Callable[[Arg1, Arg2], ReturnType]는 호출 가능한 객체의 매개변수와 반환 타입을 명시.
  • 사용자 정의 타입: NewType을 사용하여 기존 타입에 대한 새로운 타입 이름을 생성할 수 있으며, 이는 추가적인 타입 안전을 제공.

코드를 작성하고 함수를 확인하면 타입에 대한 힌트가 보이게 된다.

함수를 작성하면, type hint가 보이게 됨

 

 

 

예시


설명하는 것들은 다음과 같다.

  • 기본 데이터 타입 : int, flaot, str, bool
  • 컨테이너 타입 : List, Dict, Tuple, Mapping, Iterable, NamedTuple
  • 복합 타입 : Optional, Union
  • 특수 타입 : Callable, NewType

 

기본 데이터 타입(int, float, str, bool)

def practice(a: int, b: float, c: str, d: bool) -> None:
    print(a, b, c, d)


practice(3, 3.14, "pi", True)
>>> 3 3.14 pi True

 

 

컨테이너 타입(List, Dict, Tuple, Mapping, Iterable, NamedTuple)

typing.List

List[T]는 특정 타입 T의 요소들로 구성된 리스트를 나타낸다.

from typing import List

def process_numbers(numbers: List[int]) -> int:
    """리스트에 포함된 숫자들의 합을 반환합니다."""
    return sum(numbers)

# 함수 사용 예시
result = process_numbers([1, 2, 3, 4, 5])
print(result)  
>>> 15

 

typing.Tuple

Tuple[T, ...]는 타입 T의 요소들로 구성되지만, 요소의 수가 고정되지 않은 튜플을 나타낸다.

from typing import Tuple

def multiply_elements(values: Tuple[int, ...]) -> int:
    """튜플 내 모든 정수의 곱을 반환합니다."""
    result = 1
    for value in values:
        result *= value
    return result

# 함수 사용 예시
result = multiply_elements((1, 2, 3, 4))
print(result) 
>>> 24

 

typing.Dict

Dict[K, V]는 키의 타입이 K이고 값의 타입이 V인 딕셔너리를 나타낸다. 예를 들어, 문자열 키와 정수 값으로 구성된 딕셔너리에 대한 타입 힌트는 다음과 같다.

from typing import Dict

def total_scores(scores: Dict[str, int]) -> int:
    """딕셔너리에 저장된 모든 점수의 합을 반환합니다."""
    return sum(scores.values())

# 함수 사용 예시
scores = {"Alice": 90, "Bob": 85, "Charlie": 88}
total = total_scores(scores)
print(total)  
>>> 263

 

typing.Mapping

Mapping은 키와 값의 쌍으로 데이터를 저장하는 구조를 나타내며, dict와 유사하지만 보다 일반적인 인터페이스를 제공한다. 

from typing import Mapping

def show_mapping(data: Mapping[str, int]) -> None:
    for key, value in data.items():
        print(f"{key}: {value}")

show_mapping({'one': 1, 'two': 2}) 
>>> one: 1, two: 2

 

typing.Iterable

Iterable은 요소를 하나씩 반환할 수 있는 컨테이너 타입을 나타낸다. 이는 리스트, 세트, 튜플 등과 같은 여러 컬렉션 타입을 포함할 수 있다. 간단히 말하면 len 함수를 사용할 수 있는 Type은 가능하다고 볼 수 있다. 

from typing import Iterable

def process_values(values: Iterable[int]) -> int:
    return sum(values)

# 리스트를 함수에 전달
print(process_values([1, 2, 3, 4]))  
>>> 10

 

typing.NamedTuple

NamedTuple은 튜플의 하위 클래스를 만들어 필드에 이름을 부여할 수 있게 한다. 이는 튜플의 각 요소에 접근할 때 인덱스 대신 이름을 사용할 수 있게 해서 코드의 가독성을 높인다.

from typing import NamedTuple

class Employee(NamedTuple):
    name: str
    id: int

# 사용 예시
emp = Employee("Alice", 100)
print(emp.name)  
>>> 'Alice'

 

복합 타입(Optional, Union)

typing.Optional ( = ' | ')

Optional[T] 타입은 변수가 None이거나 T 타입의 값을 가질 수 있음을 나타내며 기술적으로 Optional[T]는 Union[T, None], T | None과 동일하다.

from typing import Optional

def greet(name: Optional[str] = None) -> str:
    if name is None:
        return "Hello, Guest!"
    else:
        return f"Hello, {name}!"
        
# 함수 사용 예시
print(greet("Alice"))
>>> 'Hello, Alice!'
def greet(name: Union[str, None] = None) -> str:
    if name is None:
        return "Hello, Guest!"
    else:
        return f"Hello, {name}!"
from typing import Optional

def greet(name: str | None = None) -> str:
    if name is None:
        return "Hello, Guest!"
    else:
        return f"Hello, {name}!"

 

typing.Union

Union 타입은 변수가 가질 수 있는 여러 타입 중 하나임을 나타내는 타입 힌트다. Union은 변수가 여러 다른 타입 중 하나를 가질 수 있음을 명시할 때 사용된다.

from typing import Union

def length_or_value(data: Union[str, int]) -> int:
    if isinstance(data, str):
        return len(data)
    elif isinstance(data, int):
        return data

# 함수 사용 예시
print(length_or_value("hello"))
>>> 5
print(length_or_value(123))  
>>> 123

 

특수 타입(Callable, NewType)

typing.Callable

Callable 타입 힌트는 함수의 인터페이스를 명확히 하여, 다른 개발자가 해당 함수의 사용 방법을 쉽게 이해할 수 있도록 한다.

구조는 다음과 같다.

Callable[[ArgType1, ArgType2, ..., ArgTypeN], ReturnType]

여기서 ArgType은 함수의 입력을 의미하고 ReturnType은 함수 출력의 타입을 의미한다.

예시를 작성하면 다음과 같다.

from typing import Callable, Union

def operate_advanced(func: Callable[[int, float], Union[int, float]], x: int, y: float) -> Union[int, float]:
    """함수 func에 정수 x와 부동소수점 y를 전닯하고 결과를 반환"""
    return func(x, y)

def multiply(a: int, b: float) -> float:
    """정수와 부동소수점 수의 곱을 반환"""
    return a * b

# `multiply` 함수를 `operate_advanced`에 인자로 전달
result = operate_advanced(multiply, 4, 0.5)
print(result)  
>>> 2.0

operate_advanced(func: Callable[[int, flaot], Union[int, float]], x: int, y: float)

이 부분에서 Callable 이후 첫번째 요소는 함수의 입력들을 의미하며 각각 int, float이 입력으로 들어가고 출력은 int와 float 요소 중 하나를 반환한다는 것을 의미한다. Callable이 끝난 이후 function parameter에 대한 Type hint를 알려준다. 

 

typing.NewType

NewType을 사용하면 기존 타입에 기반한 새로운 타입을 생성할 수 있다. 이는 타입 체크 시에 기존 타입과 구별된다.

from typing import NewType

UserId = NewType('UserId', int)

def get_user_name(user_id: UserId) -> str:
    return "John Doe"  # 예시 목적

# 사용 예시
user = UserId(123)
print(get_user_name(user))  
>>> "John Doe"
728x90