PEP 484에 λŒ€ν•œ μ†Œκ°œ

Python 3.5 λ²„μ „μ—λŠ” λ‹€μŒκ³Ό 같은 ν˜•μ‹μœΌλ‘œ IDE와 μ½”λ“œ 가독성에 도움을 쀄 수 μžˆλ„λ‘ ν•¨μˆ˜μ˜ μΈμžμ™€ λ°˜ν™˜κ°’μ— λŒ€ν•œ νƒ€μž… νžŒνŠΈκ°€ 처음으둜 λ„μž… λ˜μ—ˆλ‹€.

def greeting(name: str) -> str:
    return 'Hello ' + name

그리고 후에 λ‚˜μ˜¨ 3.6 λ²„μ „μ—μ„œλŠ” μΈμžμ™€ λ°˜ν™˜κ°’ 만이 μ•„λ‹ˆλΌ λ³€μˆ˜μ—λ„ νƒ€μž… 힌트 ν‘œκΈ°κ°€ κ°€λŠ₯ν•΄μ‘Œλ‹€.

def greeting(name: str) -> str:
    s: str = 'Hello ' + name
    return s

νƒ€μž… 힌트의 μ˜μ˜μ™€ λͺ©ν‘œ

νƒ€μž… 힌트 κΈ°λŠ₯은 νƒ€μž… ν‘œμ‹œμ— κ΄€ν•œ ν‘œμ€€ ꡬ문을 μ œκ³΅ν•˜κ³ , 더 μ‰¬μš΄ 정적 뢄석과 λ¦¬νŒ©ν† λ§ 및 νƒ€μž… 정보λ₯Ό μΆ”λ‘ ν•˜λŠ” 것에 λŒ€ν•œ 도움을 μ£ΌκΈ° μœ„ν•΄ λ§Œλ“€μ–΄μ‘Œλ‹€.

예λ₯Ό λ“€μ–΄ μ˜ˆμƒν•˜μ§€ λͺ»ν•œ νƒ€μž…μ΄ λ³€μˆ˜μ— 할당될 λ•Œλ‚˜ ν•¨μˆ˜μ— 전달될 λ•Œ IDEλ‚˜ 정적 κ²€μ‚¬κΈ°λŠ” μ‰½κ²Œ 였λ₯˜λΌκ³  νŒλ‹¨ν•  수 μžˆμ„κ²ƒμ΄λ‹€. λ˜ν•œ λ‹€λ₯Έ μ‚¬λžŒμ΄λ‚˜, μ‰½κ²Œ μžŠμ–΄λ²„λ¦΄κ²ƒ 같은 μ½”λ“œμ— μ–΄λ–€ νƒ€μž…μ΄ κΈ°λŒ€λ˜λŠ”μ§€ μ‰½κ²Œ μ•Œλ €μ€„ 수 μžˆλ‹€.

νƒ€μž… νžŒνŠΈλŠ” μ μ ˆν•œ 도ꡬ와 ν•¨κ»˜ μ‚¬μš©λ  경우 정적 μ–Έμ–΄κ°€ κ°€μ§€λŠ” μž₯점인 νƒ€μž… μ‹œμŠ€ν…œμ˜ 견고함을 동적 μ–Έμ–΄λ‘œμ¨ μ‘°κΈˆμ΄λΌλ„ λ”°λΌμž‘μ„ 수 μžˆλ„λ‘ 도와쀄 것이닀.

κ·ΈλŸ¬λ‚˜ 파이썬이 정적 νƒ€μž…μ„ 지ν–₯ν•˜λŠ” 것은 μ•„λ‹ˆλ‹€.

νƒ€μž… 힌트 κΈ°λŠ₯은 말 κ·ΈλŒ€λ‘œ νƒ€μž… "힌트" κΈ°λŠ₯일 뿐이닀. νƒ€μž… νžŒνŠΈλŠ” 정적 검사기와 IDEλ₯Ό μ‚¬μš©ν•˜λ©° μ½”λ“œμ˜ μ§ˆμ„ 높이기 μœ„ν•΄ μ‚¬μš© 될 수 μžˆμœΌλ‚˜ κ²°μ½” λŸ°νƒ€μž„μ— 영ν–₯을 λΌμΉ˜μ§€ μ•ŠλŠ”λ‹€. μ •μˆ˜ν˜•μ„ κ°€μ§ˆ λ³€μˆ˜μ— λ¬Έμžμ—΄ νƒ€μž…μ„ 힌트둜 μž‘μ„±ν•΄ λ†“λŠ”λ‹€κ³  ν•΄μ„œ νŒŒμ΄μ¬μ€ μ•„λ¬΄λŸ° λ¬Έμ œκ°€ μžˆλ‹€κ³  μƒκ°ν•˜μ§€ μ•Šμ„ 것이닀.

사싀 νƒ€μž… νžŒνŠΈλŠ” μ½”λ“œμ— 뢙은 주석에 가깝닀. λ…μŠ€νŠΈλ§μ„ doc을 μ‚¬μš©ν•˜μ—¬ κ°€μ Έμ˜¬ 수 μžˆλŠ” 것 처럼 νƒ€μž… 힌트 정보 λ˜ν•œ annotations속성을 ν†΅ν•˜μ—¬ νƒ€μž… 힌트λ₯Ό κ°€μ Έμ˜¬ 수 μžˆλ‹€.

νƒ€μž… 힌트λ₯Ό ν‘œν˜„ν•˜λŠ” 문법

파이썬의 νƒ€μž… νžŒνŠΈλŠ” typing λͺ¨λ“ˆμ„ μ‚¬μš©ν•˜μ—¬ μž‘μ„±ν•  수 μžˆλ‹€.

κ°„λ‹¨ν•œ νƒ€μž… ν‘œκΈ°

λ¨Όμ € ν•¨μˆ˜ 선언뢀에 κ΄€ν•œ νƒ€μž… νžŒνŠΈλŠ” 인수 뒀에 μ½œλ‘ μ„ λΆ™μ—¬μ„œ 인수의 νƒ€μž… 힌트λ₯Ό 뢙이고, κ΄„ν˜Έ λ’€ 콜둠 전에 "-> νƒ€μž…" 을 λΆ™μ΄λŠ” ν˜•μ‹μœΌλ‘œ λ°˜ν™˜κ°’μ— λŒ€ν•œ νƒ€μž… 힌트λ₯Ό 지정할 수 μžˆλ‹€.

def make_post(title: str, author_id: int=0) -> str:
    ...

λ³€μˆ˜μ˜ νƒ€μž…μ€ ν•¨μˆ˜ μΈμžμ™€ λΉ„μŠ·ν•œ ν˜•μ‹μœΌλ‘œ 힌트λ₯Ό 뢙일 수 μžˆλ‹€.

num: int = 34  # int type
string: str = "Hello types!"  # str type
test: Test = Test(num)  # class "Test" type

클래슀 멀버 λ³€μˆ˜λ„ λ³€μˆ˜μ™€ λΉ„μŠ·ν•˜λ‹€.

class A:
    x: int  # int type
    y: str  # str type
    z: float  # float type

    def __init__(self, x: int, y: str, z: float):
        self.x = x
        self.y = y
        self.z = z

νŠΉλ³„ν•œ νƒ€μž…λ“€

타이핑 λͺ¨λ“ˆμ—λŠ” νŠΉλ³„ν•œ νƒ€μž…λ“€ λͺ‡κ°€μ§€κ°€ μ‘΄μž¬ν•œλ‹€.
κ·Έ 쀑 Any와 NoReturn에 λŒ€ν•΄ μ†Œκ°œν•œλ‹€.

AnyλŠ” 말 κ·ΈλŒ€λ‘œ λͺ¨λ“  νƒ€μž…μ„ ν—ˆμš©ν•œλ‹€.

x: Any = 3  # λœλ‹€
y: Any = "anyone"  # λœλ‹€

NoReturn은 리턴이 λ˜λŠ”κ²ƒμ΄ μ•„λ‹ˆλΌ μ˜ˆμ™Έλ₯Ό λ°œμƒμ‹œν‚€λŠ” λ“±μ˜ κ²½μš°μ— μ‚¬μš©ν•œλ‹€.

from typing import NoReturn

def stop() -> NoReturn:
    raise RuntimeError('no way')

νƒ€μž… 별칭

νƒ€μž… 별칭은 κ°„λ‹¨ν•˜λ‹€. νƒ€μž…μ„ μƒˆ λ³€μˆ˜μ— λŒ€μž…ν•˜λ©΄ κ·Έ λ³€μˆ˜λŠ” νƒ€μž… λ³„μΉ­μœΌλ‘œμ¨ κΈ°λŠ₯ν•œλ‹€. λ‹€μŒμ€ κ°„λ‹¨ν•œ μ˜ˆμ‹œμ΄λ‹€.

from typing import List

url_ls = List[str]  # List with str type 
crawling_result = List[str]  # List with str type 

def crawler(urls: url_ls) -> crawling_result:
    ...

λ¬Όλ‘  λ”μš± λ³΅μž‘ν•œ νƒ€μž… 별칭도 λ§Œλ“€ 수 μžˆλ‹€. 이 μ½”λ“œλŠ” μ œλ„€λ¦­μ„ ν™œμš©ν•œ κ²ƒμœΌλ‘œ 좔후에 더 μ•Œμ•„λ³Ό 것이닀.

from typing import TypeVar, Tuple, Iterable

T = TypeVar('T', int, float)
Vector = Iterable[Tuple[T, T]]

def inproduct(v: Vector[T]) -> T:  # λ²‘ν„°μ˜ 내적
    return sum(x*y for x, y in v)

객체둜써의 ν•¨μˆ˜μ˜ νƒ€μž…

ν•¨μˆ˜λ₯Ό μž‘μ„±ν•˜λ‹€ 보면 ν•¨μˆ˜λ₯Ό λ°˜ν™˜ν•˜λŠ” ν•¨μˆ˜ λ˜λŠ” ν•¨μˆ˜λ₯Ό ν•¨μˆ˜λ₯Ό μž‘μ„±ν•˜κ²Œ 될 것이닀. λ˜λŠ” λ³€μˆ˜μ— ν•¨μˆ˜ 객체λ₯Ό ν• λ‹Ήν•  μˆ˜λ„ μžˆλ‹€. μ΄λŸ¬ν•œ 객체둜써의 ν•¨μˆ˜μ˜ νƒ€μž…μ€ Callable을 μ‚¬μš©ν•˜μ—¬ ν‘œν˜„ν•  수 μžˆλ‹€.

from typing import Callable

def callback_loader(callback: Callable[[float], int]) -> int:
    # float을 인수둜 λ°›μ•„ int ν˜•μ„ λ°˜ν™˜ν•˜λŠ” μ½œλ°±μ„ 인수둜 λ°›λŠ”λ‹€.
    return callback(3.7)
from typing import Callable

def closure(txt: str) -> Callable[[], str]:
    # 아무 μΈμˆ˜λ„ 받지 μ•Šκ³  str ν˜•μ„ λ°˜ν™˜ν•˜λŠ” ν•¨μˆ˜λ₯Ό λ°˜ν™˜ν•œλ‹€.
    def inner_func() -> str:
        return txt

    return inner_func
from typing import Callable

def func(txt: str) -> str:
    return txt

x: Callable[[str], str] = func  # ν•¨μˆ˜ 객체λ₯Ό ν• λ‹Ή

클래슀 νƒ€μž…

파이썬의 νƒ€μž… νžŒνŠΈλŠ” νŠΉμ •ν•œ ν΄λž˜μŠ€λΌλŠ” 것 λ˜ν•œ λͺ…μ‹œν•  수 μžˆλ‹€.

class A:
    def print_all() -> None:
        ...

def print_class(cls_obj: A) -> None:
    cls_obg.print_all()

a = A()
print_class(a)  # 정적 검사가 톡과할 것이닀.

그런데 λ§Œμ•½ 상속받은 ν•˜μœ„ ν΄λž˜μŠ€λ„ 받아듀이렀면 μ–΄λ–»κ²Œ ν•΄μ•Ό ν• κΉŒ?

class A:
    def print_all() -> None:
        ...

class B(A):
    ...

def print_class(cls_obj: A) -> None:
    cls_obg.print_all()

b = B()
print_class(b)  # 정적 κ²€μ‚¬κΈ°λŠ” νƒ€μž…μ΄ μΌμΉ˜ν•˜μ§€ μ•ŠλŠ”λ‹€κ³  κ²½κ³ ν•  것이닀.

이럴 λ•Œλ₯Ό μœ„ν•΄ Type[C] 문법이 μ€€λΉ„λ˜μ–΄ μžˆλ‹€. (CλŠ” 클래슀λ₯Ό λ‚˜νƒ€λ‚Έλ‹€) κ·Έλƒ₯ Cλ₯Ό νƒ€μž…μœΌλ‘œ μ‚¬μš©ν•˜λ©΄ C의 μΈμŠ€ν„΄μŠ€λ§Œμ„ λ°›μ•„λ“€μ΄μ§€λ§Œ Type[C]을 μ‚¬μš©ν•˜λ©΄ μƒμœ„ 클래슀λ₯Ό 상속받은 λͺ¨λ“  ν•˜μœ„ 클래슀 λ˜ν•œ ν—ˆμš©ν•˜κ²Œ λœλ‹€.

from typing import Type

class A:
    def print_all() -> None:
        ...

class B(A):
    ...

def print_class(cls_obj: Type[A]) -> None:
    # A의 ν•˜μœ„ ν΄λž˜μŠ€λ„ ν—ˆμš©
    cls_obg.print_all()

b = B()
print_class(b)  # 정적 검사가 톡과할 것이닀.

μ œλ„€λ¦­

데이터 ν˜•μ‹μ— μ˜μ‘΄ν•˜μ§€ μ•Šκ³  인자, λ³€μˆ˜ λ˜λŠ” λ°˜ν™˜κ°’ 등이 μ—¬λŸ¬ λ‹€λ₯Έ 데이터 νƒ€μž…λ“€μ„ κ°€μ§ˆ 수 μžˆλŠ” 방식을 μ œλ„€λ¦­μ΄λΌκ³  ν•œλ‹€.

파이썬의 νƒ€μž… 힌트 κΈ°λŠ₯μ—μ„œλ„ μ œλ„€λ¦­ ν‘œν˜„μ΄ κ°€λŠ₯ν•˜λ‹€.

λ‹€μŒμ€ κ°„λ‹¨ν•œ μ œλ„€λ¦­μ„ μ‚¬μš©ν•œ 클래슀의 μ„ μ–Έκ³Ό μ‚¬μš©μ˜ μ˜ˆμ‹œμ΄λ‹€.

from typing import TypeVar, Generic, List

T = TypeVar('T')

class C(Generic[T]):
    def __init__(self) -> None:
        self.ls: List[T] = []
        # T νƒ€μž…μ˜ 리슀트λ₯Ό μ΄ˆκΈ°ν™”ν•œλ‹€

    def put(item: T) -> None:
        # T νƒ€μž…μ„ 인수둜 λ°›λŠ”λ‹€
        self.ls.append(item)

    def get(index: int) -> T:
        # T νƒ€μž…μ„ λ°˜ν™˜ν•œλ‹€
        return self.ls[index]

c = C[str]()  # νƒ€μž…μ€  str둜 κ²°μ •λœλ‹€
c.put("hello")  # μ•„λ¬΄λŸ° λ¬Έμ œκ°€ μ—†λ‹€
c.get(0)  # str νƒ€μž…μ˜ 값을 λ°˜ν™˜ν•  κ²ƒμœΌλ‘œ κΈ°λŒ€λœλ‹€.

ν•¨μˆ˜μ—λ„ μ œλ„€λ¦­μ„ μ μš©ν•  수 μžˆλ‹€.

from typing import TypeVar, Sequence

T = TypeVar('T')

def first(sqnce: Sequence[T]) -> T:
    # μž…λ ₯받은 μ‹œν€€μŠ€ 객체의 νƒ€μž…μ— 따라 λ°˜ν™˜ νƒ€μž…λ„ κ²°μ •λœλ‹€.
    return sqnce[0]

μœ λ‹ˆμ˜¨

μ œλ„€λ¦­κ³Ό 달리 ν—ˆμš© κ°€λŠ₯ν•œ νƒ€μž…μ˜ λ²”μœ„κ°€ μ •ν•΄μ Έ μžˆλ‹€λ©΄ Union을 μ‚¬μš©ν•  수 μžˆλ‹€.
Union은 μ œν•œλœ νƒ€μž…μ˜ 집합이닀. Union이 νƒ€μž… 힌트둜 μ‚¬μš©λœλ‹€λ©΄ ν•΄λ‹Ή λ³€μˆ˜-인수-리턴값은 ν•΄λ‹Ή Union에 μ†ν•΄μžˆλŠ” νƒ€μž…μ„ κ°€μ§ˆμˆ˜ μžˆλ‹€κ³  ν‘œν˜„λœλ‹€

from typing import Union

...

def dispencer(select: int) -> Union[Coke, Soda]:
    # Cokeλ₯Ό 리턴할 μˆ˜λ„ 있고, Sodaλ₯Ό 리턴할 μˆ˜λ„ μžˆλ‹€.
    ...

μ˜€λ²„λ‘œλ”©

μ˜€λ²„λ‘œλ”©μ΄λž€ 같은 ν•¨μˆ˜ 이름을 κ°€μ§€μ§€λ§Œ μΈμˆ˜κ°€ λ‹€λ₯Έ ν•¨μˆ˜λ₯Ό μ„ μ–Έν•  수 μžˆλŠ” 방법을 λ§ν•œλ‹€. λ¬Όλ‘  νŒŒμ΄μ¬μ€ μ˜€λ²„λ‘œλ”©μ„ μ§€μ›ν•˜μ§€ μ•Šμ§€λ§Œ typing λͺ¨λ“ˆμ˜ @overload λ°μ½”λ ˆμ΄ν„°λ₯Ό μ‚¬μš©ν•˜μ—¬ μ˜€λ²„λ‘œλ”©μ΄ κ°€λŠ₯ν•œ κ²ƒμ²˜λŸΌ 보이게 ν•  수 μžˆλ‹€.

μ˜€λ²„λ‘œλ“œ λ°μ½”λ ˆμ΄ν„°λ₯Ό μ‚¬μš©ν•˜κΈ° μœ„ν•΄μ„œλŠ” μ˜€λ²„λ‘œλ“œ λ°μ½”λ ˆμ΄ν„°λ₯Ό μ μš©ν•œ ν•¨μˆ˜ μ›ν˜•μ„ λ§Œλ“€κ³ , μ˜€λ²„λ‘œλ“œ λ°μ½”λ ˆμ΄ν„°κ°€ μ μš©λ˜μ§€ μ•Šμ€ 본체λ₯Ό λ§Œλ“€μ–΄μ•Ό ν•œλ‹€.

μ˜€λ²„λ‘œλ“œ λ°μ½”λ ˆμ΄ν„°κ°€ 적용된 μ½”λ“œλ₯Ό μ‹€ν–‰μ‹œμΌœ 보면 λ°μ½”λ ˆμ΄ν„°κ°€ μ—†λŠ” 본체만 μ‹€ν–‰λœλ‹€.

from typing import overload, Union

class MyIter:

    @overload
    def __getitem__(self, i: int) -> int: ...

    @overload
    def __getitem__(self, s: slice) -> bytes: ...

    def __getitem__(self, x: Union[int, slice]) -> Union[int, bytes]:
        if isinstance(x, int):
            pass
        elif isinstance(x, slice):
            pass

μ „λ°© μ°Έμ‘°

ν˜„μž¬(3.7 λ²„μ „κΉŒμ§€)의 νƒ€μž… 힌트 κΈ°λŠ₯을 λ‹€μŒκ³Ό 같은 μ½”λ“œλ₯Ό μ‚¬μš©ν•  수 μ—†λ‹€. 힌트λ₯Ό 평가할 λ•Œ 아직 μ •μ˜λ˜μ§€ μ•Šμ€ νƒ€μž…μ„ μ‚¬μš©ν•  수 μ—†κΈ° λ•Œλ¬Έμ΄λ‹€.

class Node:
    def __init__(self, right: Node, left: Node):
        # 아직 Node의 μ •μ˜κ°€ λλ‚˜μ§€ μ•Šμ•„ 였λ₯˜κ°€ λ°œμƒν•œλ‹€.
        self.right = right
        self.left = left

이 λ¬Έμ œμ—λŠ” λ‘κ°€μ§€μ˜ ν•΄κ²° λ°©μ•ˆμ΄ μ‘΄μž¬ν•œλ‹€. νƒ€μž… μ •μ˜λ₯Ό λ‚˜μ€‘μ— 확인할 μˆ˜μžˆλŠ” λ¬Έμžμ—΄ λ¦¬ν„°λŸ΄λ‘œ μ²˜λ¦¬ν•˜λ˜κ°€, from __future__ import annotations ꡬ문(파이썬 3.7 만 ν•΄λ‹Ή)을 파일 맨 μœ„μ— μ‚½μž…ν•˜μ—¬ νƒ€μž… νžŒνŠΈμ— λŒ€ν•œ 평가λ₯Ό lazy ν•˜κ²Œ ν•¨μœΌλ‘œμ¨ ν•΄κ²°ν•  수 μžˆλ‹€.

class Node:
    def __init__(self, right: 'Node', left: 'Node'):
        self.right = right
        self.left = left
from __future__ import annotations

class Node:
    def __init__(self, right: Node, left: Node):
        self.right = right
        self.left = left

μ œλ„€λ ˆμ΄ν„°μ™€ 코루틴

μ œλ„€λ ˆμ΄ν„° ν•¨μˆ˜λŠ” Generator[yield_type, send_type, return_type]의 ν˜•μ‹μœΌλ‘œ νƒ€μž… 힌트λ₯Ό κ°€μ§ˆ 수 μžˆλ‹€.

def echo_round() -> Generator[int, float, str]:
    res = yield
    while res:
        res = yield round(res)
    return 'OK'

κ·ΈλŸ¬λ‚˜ 비동기 ν•¨μˆ˜(코루틴)은 쑰금 νŠΉλ³„ν•˜λ‹€. 비동기 ν•¨μˆ˜λ₯Ό ν•˜λ‚˜ μ„ μ–Έν•΄ 보자.

async def test():
    return "Hello async!"

λ§Œμ•½ 이 ν•¨μˆ˜κ°€ 무쑰건 await λœλ‹€λ©΄ 이 ν•¨μˆ˜μ˜ κ²°κ³ΌλŠ” str νƒ€μž…μ„ κ°€μ§€κ²Œ 될 κ²ƒμ΄μ§€λ§Œ await λ˜μ§€ μ•ŠλŠ”λ‹€λ©΄ 코루틴 자체λ₯Ό λ°˜ν™˜ν•˜κ²Œ 될 것이닀.
비동기 ν•¨μˆ˜μ˜ νƒ€μž… νžŒνŠΈλŠ” λ‹€μŒκ³Ό 같이 μ‚¬μš©ν•˜μž.

from typing import Corutine  # Generator와 μ‚¬μš©λ²•μ€ κ°™λ‹€.

async def test() -> Corutine[Any, Any, str]:
    return "Hello async!"

async def run():
    res: Corutine[Any, Any, str] = test()
    res_2: str = await test()

이λ₯Ό μš©μš©ν•΄μ„œ 비동기 ν•¨μˆ˜ 객체λ₯Ό ν‘œν˜„ν•  μˆ˜λ„ μžˆλ‹€

from typing import Corutine, Callable

async def test() -> Corutine[Any, Any, str]:
    return "Hello async!"

x: Callable[[], Corutine[Any, Any, str]] = test

λ§ˆμΉ˜λ©΄μ„œ

μ—¬κΈ°κΉŒμ§€ 파이썬의 νƒ€μž… νžŒνŠΈμ— λŒ€ν•΄ μ•Œμ•„λ³΄μ•˜λ‹€. PEP 484μ—μ„œ λ°νžˆλ“― νƒ€μž… νžŒνŠΈλŠ” 파이썬이 정적 μ–Έμ–΄λ‘œ 쓰이기 μœ„ν•œ 것도 μ•„λ‹ˆκ³ , ν•„μˆ˜λ„ μ•„λ‹ˆλ‹€. κ·ΈλŸ¬λ‚˜ μ μ ˆν•œ 도ꡬ와 같이 μ‚¬μš©ν•œλ‹€λ©΄ 점점 μ»€μ§€λŠ” μ½”λ“œλ₯Ό λ§Œλ“€μ–΄ κ°€λŠ” 것을 도와쀄 수 μžˆμ„ 것이닀.