Python은 동적 프로그래밍 언어이기 때문에 코드를 실행하면 해당 변수의 타입을 추론하여 체크한다. 변수의 타입은 정해져 있지 않기 때문에 개발자가 자유롭게 사용할 수 있다는 장점이 있지만, 프로젝트의 규모가 커짐에 따라서 애플리케이션의 안정성에 위험요소가 될 뿐만 아니라 치명적인 버그를 만들 수 있다.
따라서 Python개발자들은 이런 요소를 신경 쓰기 위하여, Python version 3.5에서부터 타입 어노테이션, 타입 힌트를 지원하고 있다.
Python Type Hinting
파라미터와 리턴값의 데이터값을 명시적을 지정해 주는 것을 의미한다. 원래는 이런 데이터값을 명시적으로 지정줄 때, 주석을 이용하여 처리를 하였으나, 개발자들에게 번거로웠을뿐더러 일반 주석과 구분이 어려웠기 때문에 좋은 평가를 받지 못했다.
그래서 타입 어노테이션(annotation)이라는 새로운 방법으로 파이썬 코드의 타입 표시를 표준화하는 형식을 통해 타입을 추론할 수 있도록 개발했다. 해당 방법을 사용하여 타입 힌팅을 한다면, IDE에서 재해석되거나 자동 완성, 정적 타입 체킹에 활용할 수 있다.
🖥️ code.py // Type Hinting 예제
num:int = 10
name:str = "Jheaon"
def helloworld(repeat:int)->str:
return "hello world"
- 말 그대로 Tpye Hinting 이기 때문에, 변수나 함수에 추가한 타입 어노테이션이 부정확해도 실행상 어떤 오류를 발생시키지 않는다.
다음은 어떻게 타입 힌팅을 사용하는지에 대해서 살펴보자.
변수에 타입 힌팅 정의
name: str = "JHeaon"
age: int = "27"
email: list = ["j3heawon@naver.com", 'j3heawon@gmail.com']
함수에 타입 힌팅 정의
반환 값에는 -> 화살표를 이용하여 처리한다.
def sum(a: int, b: int) -> int:
return a + b
📢 클론(:) 과 화살표(->) 사용할 때는 Python 관행에 따라 클론은 뒤에 한 칸, 화살표는 앞뒤로 한 칸씩 뛰어 코드를 작성한다.
복잡한 객체에 대한 타입 힌팅
일반적으로 자주 사용하는 list, dict, tuple, set은 Typing이라는 모듈을 이용해 처리한다.
from typing import List, Dict, Tuple, Set
nums: List[int] = [1, 2, 3]
person: Dict[str, str] = {"name":"heaon", "email":"j3heawon@naver.com"}
user: Tuple[int, str, bool] = (3, "JHeaon", False)
chars: Set[str] = {"A", "B", "C"}
🎆 파이썬 공식 문서에 따르면 3.9 버전 이상부터는 typing 모듈로 따로 불러내지 않고, 기본 내장되어 있는 객체형을 통해 타입 힌팅 처리를 할 수 있다.
nums: list[int] = [1, 2, 3]
person: dict[str, str] = {"name":"heaon", "email":"j3heawon@naver.com"}
user: tuple[int, str, bool] = (3, "JHeaon", False)
chars: set[str] = {"A", "B", "C"}
매개변수로 함수가 들어 갈때 타입 힌팅
typing 모듈에서 Callable을 이용하여 처리한다. Callable[[들어가는 인자], 반환값] 형태로 작성한다.
from typing import Callable
# Callable 함수는 str 인자를 받고 반환값으로 str을 return 하는 함수이다.
def repeat(greet: Callable[[str], str], name: str, times: int = 2) -> None:
for _ in range(times):
print(greet(name))
변수에 대한 여러 타입의 타입 힌팅
한 개의 변수 값에서 여러 개의 타입이 허용될 수 있는 상황에 사용한다.
from typing import Union
def toString(num: Union[int, float]) -> str:
return str(num)
📢 Python 3.10 버전 이후부터는 Union 모듈을 따로 불러낼 필요 없이 | 을 통해서 중복 타입을 허용할 수 있음을 표기할 수 있다.
def toString(num: int|float) -> str:
return str(num)
None이 허용되는 타입에 대한 타입 힌팅
None이 허용되는 함수의 매개 변수에 대한 타입을 명시할 때 사용한다.
from typing import Optional
def repeat(message: str, times: Optional[int] = None) -> list:
if times:
return [message] * times
else:
return [message]
-📢 Python 3.10 버전 이후부터는 Optional 모듈을 따로 불러낼 필요없이 | 을 통해서 None을 허용할 수 있음을 표기할 수 있다.
def repeat(message: str, times: int|None) -> list:
if times:
return [message] * times
else:
return [message]
재할당이 불가능한 상수 혹은 변수에 대한 타입힌팅
재할당이 불가능 한 const 변수에 사용 한다..
from typing import Final
TIME_OUT: Final[int] = 10
제네릭 프로그래밍
제네릭 프로그래밍이란 파라미터의 타입을 나중에 지어 되게 하여 재활용성을 높이는 프로그래밍 스타일을 뜻한다. 이게 좀 많이 어려운 부분인데 공식문서의 코드를 가져와서 살펴보면 다음과 같다.
from typing import TypeVar, Generic
from logging import Logger
# TypeVar을 통해 T라는 타입을 생성한다.
# T = TypeVar('T', int, float) 등 여러가지 사용 가능
T = TypeVar('T')
class LoggedVar(Generic[T]):
"""
Generic[T] 라는 통해 LoggedVar 클래스가 매개 변수 T를 취한다는 것을 정의한다.
"""
def __init__(self, value: T, name: str, logger: Logger) -> None:
self.name = name
self.logger = logger
self.value = value
def set(self, new: T) -> None:
self.log('Set ' + repr(self.value))
self.value = new
def get(self) -> T:
self.log('Get ' + repr(self.value))
return self.value
def log(self, message: str) -> None:
self.logger.info('%s: %s', self.name, message)
즉 들어오는 타입이 뭔지 모르니 일단 T라고 가정을 한 다음 타입 T에 관하여 코드를 작성하는 기법이다. 이를 사용하면, 매번 타입을 변환할 필요가 없고, 인자의 타입에 따라 반복적으로 정의할 필요도 없어서 코드가 간결해지고 코드의 재 사용성을 높일 수 있다는 장점을 가지고 있다.
'Programing Language > Python' 카테고리의 다른 글
파이썬 파일 실행 할 때 생기는 __pycache__ 파일에 대해 알아보기 (0) | 2023.06.29 |
---|---|
윈도우, 맥에서 가상환경을 구성하고 패키지 매니저 pip와 requirements.txt 로 패키지 관리하기 (0) | 2023.06.29 |
리스트 컴프리헨션과 삼항연산자을 이용하여 코드 작성하기 (0) | 2023.06.29 |
아스테리스크와 패킹, 언패킹 기법 (0) | 2023.06.29 |
mypy, pyright 을 이용한 정적 타입 검사 (2) | 2023.06.27 |