점프 투 파이썬 DAY17(파이썬 타입 이노테이션)

정지범·4일 전

점프 투 파이썬

목록 보기
32/37

07-4 파이썬 타입 어노테이션

목차

  • 1. 동적 언어와 정적 언어
  • 2. 동적 언어의 장단점
  • 3. 타입 어노테이션이란?
  • 4. 변수에 타입 지정하기
  • 5. 함수에 타입 지정하기
  • 6. 리스트와 딕셔너리 타입 지정하기
  • 7. typing 모듈
  • 8. Optional과 Union
  • 9. Callable과 Any
  • 10. mypy를 이용한 타입 검사

1. 동적 언어와 정적 언어

파이썬은 동적 타입 언어(Dynamically Typed Language) 이다.

다음 코드를 살펴보자.

a = 1

print(type(a))

실행 결과

<class 'int'>

현재 a에는 정수 1이 저장되어 있으므로 자료형은 int이다.

이번에는 같은 변수에 문자열을 저장해 보자.

a = "1"

print(type(a))

실행 결과

<class 'str'>

처음에는

a → int

였지만 새로운 값을 대입하면서

a → str

처럼 사용할 수 있다.

즉 파이썬에서는 프로그램 실행 중에도 변수에 서로 다른 타입의 값을 대입할 수 있다.

a = 10
a = "Python"
a = [1, 2, 3]

이것이 파이썬을 동적 타입 언어라고 부르는 이유이다.


2. 정적 타입 언어

반대로 자바와 같은 언어는 정적 타입 언어(Statically Typed Language) 이다.

예를 들어 자바에서는

int a = 1;

처럼 변수의 타입을 미리 지정한다.

여기서 a는 int형이므로

a = "1";

처럼 문자열을 저장할 수 없다.

즉,

정적 타입 언어

변수 생성
   ↓
타입 지정
   ↓
지정된 타입의 값만 사용

하는 방식이다.


3. 동적 언어와 정적 언어 비교

구분동적 타입정적 타입
대표 언어PythonJava
타입 결정실행 중미리 지정
타입 변경비교적 자유로움제한됨
코드 작성빠르고 유연함타입이 명확함
타입 오류 발견실행 중 발견 가능실행 전 발견 가능

4. 동적 언어의 장점

파이썬과 같은 동적 타입 언어는 타입을 일일이 지정하지 않아도 된다.

예를 들어

name = "pingu"
age = 20
scores = [90, 80, 100]

처럼 바로 값을 저장할 수 있다.

따라서

  • 코드가 간단함
  • 빠르게 프로그램 작성 가능
  • 타입에 비교적 자유로움
  • 코드가 간결함

이라는 장점이 있다.


5. 동적 언어의 단점

하지만 프로그램의 규모가 커지면 문제가 발생할 수 있다.

다음 함수를 보자.

def add(a, b):
    return a + b

코드만 보면

a에는 어떤 값이 들어와야 하지?
b는 숫자인가?
문자열인가?
결과는 어떤 자료형이지?

를 바로 알기 어렵다.

특히 여러 사람이 함께 개발하는 큰 프로젝트에서는 타입을 잘못 사용해 버그가 발생할 가능성이 커질 수 있다.

이러한 문제를 보완하기 위해 사용하는 것이

타입 어노테이션(Type Annotation)

이다.


6. 타입 어노테이션이란?

타입 어노테이션은 변수나 함수에

어떤 타입을 사용할 것인지 표시해 주는 기능

이다.

예를 들어

num: int = 1

이라고 작성한다.

여기서

: int

부분이 타입 어노테이션이다.

뜻은

num 변수에는
int 타입의 값을 사용할 예정이다.

라고 알려 주는 것이다.


7. 타입 어노테이션은 강제 규칙이 아니다

이 부분이 매우 중요하다.

num: int = 1

이라고 작성했다고 해서 파이썬이 반드시 int만 사용하도록 강제하는 것은 아니다.

예를 들어

num: int = 1

num = "Python"

print(num)

실행 결과

Python

정상적으로 실행된다.

즉,

타입 어노테이션
     ↓
타입 강제 X
     ↓
타입에 대한 힌트 O

이다.

그래서 Type Hint라고도 부른다.


8. 변수에 타입 지정하기

기본적인 변수 타입은 다음과 같이 지정할 수 있다.

num: int = 1

name: str = "홍길동"

height: float = 175.5

is_student: bool = True

각각 의미는 다음과 같다.

코드타입
num: int정수
name: str문자열
height: float실수
is_student: bool참/거짓

9. 기본 자료형 타입 어노테이션

자주 사용하는 타입은 다음과 같다.

int   → 정수
float → 실수
str   → 문자열
bool  → 불
list  → 리스트
tuple → 튜플
dict  → 딕셔너리
set   → 집합

예를 들어

number: int = 10
name: str = "Python"
numbers: list = [1, 2, 3]

처럼 사용할 수 있다.


10. 함수에 타입 지정하기

타입 어노테이션은 함수에서도 많이 사용한다.

다음 함수를 살펴보자.

def add(a: int, b: int) -> int:
    return a + b

하나씩 보면

a: int

은

a는 int 타입

이라는 의미이다.

b: int

은

b는 int 타입

이라는 의미이다.

그리고

-> int

는

이 함수는 int 타입을 반환한다.

라는 뜻이다.


11. 함수 타입 어노테이션 구조

기본 구조는 다음과 같다.

def 함수이름(매개변수: 타입) -> 반환타입:
    ...

예를 들어

def greet(name: str) -> str:
    return f"안녕하세요, {name}님!"

뜻은

name
 ↓
str 입력

greet()
 ↓

str 반환

이다.


12. 타입 어노테이션이 없는 경우

다음 코드를 보자.

def add(a, b):
    return a + b

이 함수만 보면

a = ?
b = ?
return = ?

를 바로 알기 어렵다.

하지만

def add(a: int, b: int) -> int:
    return a + b

라고 작성하면

입력
a → int
b → int

출력
return → int

이라는 사실을 쉽게 알 수 있다.

즉 타입 어노테이션은 코드의 가독성을 높여 준다.


13. 리스트 내부 타입 지정하기

단순히

numbers: list

라고 작성하면 리스트라는 사실만 알 수 있다.

하지만 리스트 안에 어떤 타입이 들어 있는지도 지정할 수 있다.

numbers: list[int] = [1, 2, 3]

뜻은

numbers
 ↓
list
 ↓
리스트 내부 값은 int

이다.


14. 다양한 컬렉션 타입

리스트

numbers: list[int] = [1, 2, 3]
int를 저장하는 리스트

딕셔너리

user_info: dict[str, int] = {
    "age": 30
}

뜻은

key   → str
value → int

이다.


튜플

coordinates: tuple[float, float] = (3.5, 7.2)

뜻은

첫 번째 값 → float
두 번째 값 → float

이다.


집합

numbers: set[int] = {1, 2, 3}

int 값을 저장하는 집합이라는 의미이다.


15. 타입을 자세하게 적는 이유

다음 두 코드를 비교해 보자.

users: list

이 코드만 보면

리스트인 것은 알겠는데
안에 무엇이 들어 있지?

라는 의문이 생긴다.

하지만

users: list[str]

라고 작성하면

문자열을 저장하는 리스트

라는 사실을 바로 알 수 있다.

따라서 코드를 읽고 이해하기 쉬워진다.


16. typing 모듈

타입 어노테이션을 보다 다양하게 사용하기 위해

typing

모듈을 사용할 수 있다.

과거 파이썬에서는 다음과 같은 형태를 많이 사용했다.

from typing import List, Dict, Tuple

numbers: List[int] = [1, 2, 3]

user_info: Dict[str, int] = {
    "age": 30
}

17. 최신 파이썬에서는?

Python 3.9 이상에서는

List[int]
Dict[str, int]
Tuple[int, int]

대신 기본 자료형을 직접 사용할 수 있다.

list[int]
dict[str, int]
tuple[int, int]

따라서 현재는 다음처럼 작성하는 방식이 일반적이다.

numbers: list[int] = [1, 2, 3]

user_info: dict[str, int] = {
    "age": 30
}

즉

과거

List[int]
Dict[str, int]
Tuple[int, int]

에서

현재

list[int]
dict[str, int]
tuple[int, int]

형태로 사용할 수 있다.


18. typing 모듈이 여전히 필요한 경우

기본 자료형은

list[int]
dict[str, int]

처럼 사용할 수 있지만 좀 더 특별한 타입을 표현할 때는 typing 모듈을 사용할 수 있다.

대표적으로

Optional
Union
Callable
Any

등이 있다.


19. Optional

다음 상황을 생각해 보자.

사용자 이름을 저장하는 변수인데 사용자 이름이 없을 수도 있다.

즉

문자열일 수도 있음
또는
None일 수도 있음

이다.

이럴 때 Optional을 사용할 수 있다.

from typing import Optional

user_name: Optional[str] = None

Optional[str]은

str 또는 None

이라는 의미이다.


값이 있는 경우

user_name: Optional[str] = "pingu"

가능하다.


값이 없는 경우

user_name: Optional[str] = None

이것도 가능하다.

따라서

Optional[str]

      ┌─ str
      │
      └─ None

이라고 생각하면 된다.


20. Union

하나의 값에 여러 타입이 들어올 수 있다고 표시할 때 Union을 사용한다.

from typing import Union

user_id: Union[int, str]

뜻은

user_id
   ↓
int 또는 str

이다.

따라서

user_id = 100

도 가능하고

user_id = "pingu"

도 가능하다.


21. Optional과 Union 관계

사실

Optional[str]

은 개념적으로

Union[str, None]

과 같은 의미라고 이해하면 된다.

즉

Optional[str]

str 또는 None

이다.


22. 최신 파이썬 문법

최근 파이썬에서는 Union 대신 |를 사용할 수도 있다.

예를 들어

user_id: int | str

은

int 또는 str

을 의미한다.

그리고

user_name: str | None

은

str 또는 None

이라는 의미이다.

따라서 다음처럼 대응해서 이해하면 된다.

Optional[str]
      ↓
str | None

Union[int, str]
      ↓
int | str

23. Callable

함수 자체의 타입도 표현할 수 있다.

이때 Callable을 사용할 수 있다.

from typing import Callable

예를 들어

def process_data(
    callback: Callable[[int], str]
) -> str:

    return callback(42)

여기서

Callable[[int], str]

은

int를 입력받고
     ↓
str을 반환하는 함수

라는 의미이다.


구조

Callable[[매개변수 타입], 반환 타입]

예를 들어

Callable[[int, int], int]

은

int, int
   ↓
함수
   ↓
int 반환

이라는 의미이다.


24. Any

어떤 타입이 들어와도 괜찮다고 표현할 때는

Any

를 사용할 수 있다.

from typing import Any

data: Any

다음과 같은 값이 모두 가능하다.

data = 10
data = "Python"
data = [1, 2, 3]
data = {"name": "pingu"}

즉

Any
 ↓
어떤 타입이든 허용

이라는 의미이다.


25. 실무 형태 예제

예를 들어 사용자를 찾는 함수를 만들어 보자.

from typing import Optional

def find_user(user_id: int) -> Optional[dict[str, str]]:

    if user_id > 0:
        return {
            "name": "홍길동",
            "email": "hong@example.com"
        }

    return None

반환 타입을 보면

Optional[dict[str, str]]

이다.

즉 이 함수의 결과는

사용자를 찾음
      ↓
dict[str, str]

사용자를 못 찾음
      ↓
None

중 하나이다.


26. 타입 어노테이션은 실제로 검사할까?

다음 코드를 살펴보자.

def add(a: int, b: int) -> int:
    return a + b

a와 b 모두 int라고 지정했다.

그런데 다음처럼 실행해 보자.

result = add(3, 3.4)

print(result)

실행 결과

6.4

정상적으로 실행된다.

왜 그럴까?

파이썬의 타입 어노테이션은

타입 강제 기능

이 아니라

타입에 대한 정보와 힌트

이기 때문이다.


27. 타입을 잘못 넣어도 파이썬은 실행한다

다음 코드에서

def add(a: int, b: int) -> int:
    return a + b

분명 b는

int

라고 표시되어 있다.

하지만

add(3, 3.4)

처럼 float을 사용해도 파이썬은 타입 어노테이션만 보고 실행을 막지 않는다.

즉

타입 어노테이션
       ↓
사람 / IDE / 검사 도구에게 정보 제공
       ↓
파이썬 실행 자체를 강제로 제한하지 않음

이다.


28. mypy

그렇다면 타입이 제대로 사용되고 있는지 어떻게 검사할까?

대표적인 도구가

mypy

이다.

mypy는 파이썬 코드를 실행하기 전에 타입 어노테이션을 분석하는 정적 타입 검사 도구이다.


29. mypy 설치하기

mypy는 파이썬 표준 라이브러리가 아니므로 설치해야 한다.

pip install mypy

설치 후 파이썬 파일을 검사할 수 있다.

예를 들어

typing_sample.py

파일을 만들었다고 해보자.

def add(a: int, b: int) -> int:
    return a + b


result = add(3, 3.4)

print(result)

30. mypy로 검사하기

터미널에서 실행한다.

mypy typing_sample.py

그러면 mypy는

b는 int를 사용한다고 했는데
float 값이 전달되었다.

라는 문제를 발견할 수 있다.

즉

add(3, 3.4)

에서

3   → int O
3.4 → float X

가 되는 것이다.


31. 코드 수정하기

다음처럼 수정한다.

def add(a: int, b: int) -> int:
    return a + b


result = add(3, 4)

print(result)

다시 검사하면

mypy typing_sample.py

타입 문제가 없다는 결과를 확인할 수 있다.


32. 파이썬과 mypy의 차이

이 부분을 확실히 구분해야 한다.

Python 실행

python typing_sample.py
실제 프로그램 실행

mypy 검사

mypy typing_sample.py
프로그램을 실행하기 전
타입 어노테이션 검사

즉

코드 작성
   ↓
mypy
   ↓
타입 검사
   ↓
문제 수정
   ↓
Python 실행

과 같은 흐름으로 사용할 수 있다.


33. 타입 어노테이션을 사용하는 이유

타입 어노테이션의 가장 큰 목적은 코드를 이해하기 쉽게 만드는 것이다.

다음 함수를 비교해 보자.

타입 어노테이션이 없는 함수

def get_user(user_id):
    ...

이것만 보면

user_id가 숫자인가?
문자열인가?

무엇을 반환하지?

를 바로 알기 어렵다.


타입 어노테이션이 있는 함수

def get_user(user_id: int) -> dict[str, str]:
    ...

이제 코드만 보고도

입력
 ↓
user_id = int

출력
 ↓
dict[str, str]

이라는 사실을 알 수 있다.


34. 특히 팀 개발에서 유용하다

혼자 작성한 작은 프로그램이라면 자신이 작성한 코드를 기억하고 있을 수 있다.

하지만 규모가 커지면

개발자 A
개발자 B
개발자 C
개발자 D
...

여러 사람이 하나의 코드를 함께 사용한다.

이때 타입 어노테이션이 있다면 함수가 어떤 값을 요구하는지 쉽게 확인할 수 있다.

def login(
    username: str,
    password: str
) -> bool:
    ...

코드만 보고도

username → str
password → str
return   → bool

이라는 구조를 알 수 있다.


35. IDE에서도 활용된다

VSCode나 PyCharm과 같은 개발 도구는 타입 어노테이션을 이용하여

  • 자동 완성
  • 타입 정보 표시
  • 잘못된 타입 경고
  • 함수 사용 방법 표시

등을 제공할 수 있다.

따라서 타입 어노테이션은 단순한 주석보다 훨씬 유용하다.


36. 타입 어노테이션 전체 구조

변수

변수명: 타입 = 값

예

age: int = 20

함수 매개변수

def 함수(매개변수: 타입):

예

def hello(name: str):

함수 반환값

def 함수() -> 타입:

예

def get_age() -> int:

매개변수와 반환값

def add(a: int, b: int) -> int:
    return a + b

37. 여러 타입 표현하기

int 리스트

numbers: list[int]

문자열 딕셔너리

user: dict[str, str]

int 또는 str

user_id: int | str

str 또는 None

name: str | None

모든 타입

from typing import Any

data: Any

함수

from typing import Callable

callback: Callable[[int], str]

📌 핵심 정리

동적 타입 언어

파이썬은 변수에 서로 다른 타입의 값을 대입할 수 있다.

a = 1
a = "Python"

타입 어노테이션

변수나 함수가 어떤 타입을 사용할 것인지 알려 주는 타입 힌트이다.

age: int = 20

함수 타입 어노테이션

def add(a: int, b: int) -> int:
    return a + b
a      → int
b      → int
return → int

컬렉션 타입

numbers: list[int]

user: dict[str, str]

position: tuple[float, float]

numbers: set[int]

Optional

from typing import Optional

name: Optional[str]
str 또는 None

Union

from typing import Union

user_id: Union[int, str]
int 또는 str

최근 파이썬에서는 다음처럼 표현할 수도 있다.

user_id: int | str

name: str | None

Callable

from typing import Callable

callback: Callable[[int], str]
int 입력
   ↓
함수
   ↓
str 반환

Any

from typing import Any

data: Any

어떤 타입이든 사용할 수 있다는 의미이다.


mypy

타입 어노테이션이 올바르게 사용되었는지 검사하는 도구이다.

pip install mypy

검사

mypy typing_sample.py

💡 가장 중요한 부분

타입 어노테이션을 작성했다고 해서 파이썬이 타입을 강제로 제한하는 것은 아니다.

def add(a: int, b: int) -> int:
    return a + b

여기서

add(3, 3.4)

를 실행해도 파이썬 자체는 실행할 수 있다.

따라서

Type Annotation
       ↓
타입을 강제한다 X

Type Annotation
       ↓
어떤 타입을 사용할지 알려 주는 힌트 O

이다.

실제로 타입이 올바르게 사용되고 있는지 확인하려면

IDE
mypy

와 같은 도구를 함께 사용한다.


⭐ 07-4 전체 흐름

Python
  ↓
동적 타입 언어
  ↓
타입 사용이 자유로움
  ↓
코드가 커지면 타입 파악이 어려울 수 있음
  ↓
Type Annotation
  ↓
변수와 함수에 타입 정보 표시
  ↓
코드 이해가 쉬워짐
  ↓
mypy
  ↓
타입 오류를 실행 전에 검사

타입 어노테이션은 파이썬을 정적 타입 언어로 만드는 기능이 아니라, 동적 타입의 장점을 유지하면서 코드에 타입 정보를 추가하는 기능이라고 이해하면 된다.

profile
성실하자:)

0개의 댓글