CS🏅/파이썬 🖤

[Python] Typing 2편

Dobby98 2023. 5. 31. 16:29

참고한 자료

 

파이썬 Typing 파헤치기 - 심화편

지난 글 '파이썬 Typing 파헤치기 - 기초편'에서는 Typing의 기본적인 내용과 8개의 타입에 대해 다루어봤습니다. 이번 포스트에서는 조금 더 심화된 타입들에 대해 다뤄보도록 하겠습니다. 심화 타

sjquant.tistory.com

 

지난 글 

 

[Python] Typing 1편

참고자료 파이썬 Typing 파헤치기 - 기초편 동적 언어에서의 타입 검사 파이썬은 동적 언어로 잘 알려진 언어입니다. 즉, 변수의 타입을 일일이 명시하지 않아도 되고, 특정 변수의 타입이 중간에

eumgill98.tistory.com

우리는 지난 시간에 Python의 Typing기능의 간단한 기초를 알아보았다

이번 시간에는 조금더 심화 과정의 Typing을 알아볼려고 한다

 

위에 참고한 블로그 주소가 있는데

조금더 자세한 내용을 알고 싶다면 참고하길 바란다 !! 

 

1. tying.Callable[..., ReturnType]

우리는 지난 시간에 함수를 인자로 받는 경우 어떻게 typing을 해야하는지 배웠다

tying.Callable을 사용해서 [[input Type], ReturnType]을 입력해주었다

그러면 만약 input type을 신경쓰고 싶지 않다면 어떻게 해야할까

 

이때 사용할 수 있는 것이 tying.Callable[..., ReturnType]이다

쉽게 말해서 ... 을 활용해서 그냥 넘기면 된다

def on_some_event_happened(callback: Callable[..., int]) -> None:
    ...

def do_this(a: int, b: str, c:str) -> int:
    ...

on_some_event_happened(do_this)

 

2. typing.TypeVar

Python에서 여러 타입을 일반화 하는 것을 Generic type이라고 한다

이러한 Generic을 Typing 하는 방법은 TypeVar를 활용하면된다

from typing import Sequence, TypeVar, Iterable

T = TypeVar("T") 

def batch_iter(data: Sequence[T], size: int) -> Iterable[Sequence[T]]:
    for i in range(0, len(data), size):
        yield data[i:i + size]

해석하자면 위의 함수는 Input으로 Sequence[int] or Sequence[str] or Sequence[float] 등 다양한 형태의 type이 모두 들어갈 수 있고 이를 통해서 Iterable한 Sequence형태의 output이 만들어진다

 

Bound

또한 여기에 bound을 적용해서 타입의 종류를 지정해줄 수 있다

from typing import Sequence, TypeVar, Iterable, Union

T = TypeVar("T", bound=Union[int, str, bytes])


def batch_iter(data: Sequence[T], size: int) -> Iterable[Sequence[T]]:
    for i in range(0, len(data), size):
        yield data[i:i + size]

즉, 상속될 수 있는 타입을 지정해 줄 수 있는 것이다

 

여기서 TypeVar['T']에서 'T'는 해당 바운드에 해당하는 모든 type을 허용한다는 것이다

만약 'T' 대신에 들어갈 수 있는 것에는 'S'와 'A' 등의 약속과 같은 기호를 넣을 수 있다 - 반드시는 아님, 지정가능

 

S = TypeVar('S', bound=str) ## Can be any subtype of str
A = TypeVar('A', str, bytes)  # Must be exactly str or bytes

 

TypeVar의 파라미터로 들어 갈 수 있는 것을 조금더 살펴보면

class typing.TypeVar(name, *constraints, bound=None, covariant=False, contravariant=False)

여기서 name은 'T', 'S' 등이고 constraints은 위의 A를 활용한 코드에서 str, bytes를 지정해준 것으로 반드시  해당 type이어야하는 것을 의미하고 , bound는 수용할 수 있는 타입의 집합을 지정해주는 것이다

참고로 bound와 constraints는 동시에 될 수 없다

 

또한  covariant, contravariant는 아래에서 한번더 나오지만  

아래의 Stack overflow의 설명을 참고하시길..

 

Python typing what does TypeVar(A, B, covariant=True) mean?

Today I took a deep dive into Liskov's Substitution Principle and covariance/contravariance. And I got stuck on the difference between: T = TypeVar("T", bound=Union[A, B]) T = TypeVar("T", A, B,

stackoverflow.com

3. typing.Generic

위에서 배운 TypeVar를 통해서 Generic 타입의 의존관계를 Typing할 수 있었다

하지만

함수가 아니라 클래스에서 Generic 타입을 선언해줄 때는 표현하기가 어렵다

따라서 Class에서 Generic 타입을 선언해줄 때는 TypeVar와 Generic을 같이 사용해주어서 표현할 수 있다

# https://docs.python.org/3/library/typing.html#user-defined-generic-types

from typing import TypeVar, Generic
from logging import Logger

T = TypeVar('T')


class LoggedVar(Generic[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)

위 코드를 살펴 보면 Class의 get의 input ,set의 return 등이 __init__의 value 타입에 의존적이라는 것을 확인할 수 있다

 

또한 해당 클래스를 input으로 받는 메소드를 구성해줄때는 Class[type]을 표시해서 특정 type을 지정해줄 수 있다

위의 코드를 input으로 활용하는 메소드를 예시로 작성해보면 다음과 같다

 

 

# https://docs.python.org/3/library/typing.html#user-defined-generic-types

from collections.abc import Iterable

def zero_all_vars(vars: Iterable[LoggedVar[int]]) -> None:
    for var in vars:
        var.set(0)

 

또한 Pytorch의 DataLoader가 이러한 방식을 활용한 Class이다

 

GitHub - pytorch/pytorch: Tensors and Dynamic neural networks in Python with strong GPU acceleration

Tensors and Dynamic neural networks in Python with strong GPU acceleration - GitHub - pytorch/pytorch: Tensors and Dynamic neural networks in Python with strong GPU acceleration

github.com

...
T = TypeVar('T')
T_co = TypeVar('T_co', covariant=True)

class DataLoader(Generic[T_co]):
    r"""
    Data loader. Combines a dataset and a sampler, and provides an iterable over
    the given dataset.

    The :class:`~torch.utils.data.DataLoader` supports both map-style and
    iterable-style datasets with single- or multi-process loading, customizing
    loading order and optional automatic batching (collation) and memory pinning.

    See :py:mod:`torch.utils.data` documentation page for more details.

    Args:

...

여기서 T_co 와 T의 차이점은 TypeVar의  covariant True 여부이다

covariant에 대한 참고 자료

 

Python’s covariance and contravariance

(Many of the examples present are based on or from PEP 484)

blog.magrathealabs.com

 

4. typing.ParamSpec

ParamSpec은 Callable의 인자를 다른 Callable의 인자로 넘겨 줄 때 사용한다

from typing import TypeVar, Callable, ParamSpec
import logging


T = TypeVar('T')
P = ParamSpec('P')


def add_logging(f: Callable[P, T]) -> Callable[P, T]:
    '''A type-safe decorator to add logging to a function.'''
    def inner(*args: P.args, **kwargs: P.kwargs) -> T:
        logging.info(f'{f.__name__} was called')
        return f(*args, **kwargs)
    return inner


@add_logging
def add_two(x: float, y: float) -> float:
    '''Add two numbers together.'''
    return x + y


@add_logging
def send_msg(msg: str) -> None:
    print(f"I Sent {msg}")

 

쉽게 말해서 위의 코드의 add_logging의 내부 함수인 inner에 P.args와 P.kwargs를 지정해주면

아래에 add_two, send_msg에 데코레이터로 활용을 할때 해당 함수 인자의 타입을 받아 올 수 있는 것이다

 

이 기능은 파이썬  3.10 이상부터 사용할 수 있다고 한다

 

 

 

추가 DOC

 

typing — Support for type hints

Source code: Lib/typing.py This module provides runtime support for type hints. The most fundamental support consists of the types Any, Union, Callable, TypeVar, and Generic. For a specification, p...

docs.python.org

 

 

PEP 484 – Type Hints | peps.python.org

PEP 484 – Type Hints Author: Guido van Rossum , Jukka Lehtosalo , Łukasz Langa BDFL-Delegate: Mark Shannon Discussions-To: Python-Dev list Status: Final Type: Standards Track Topic: Typing Created: 29-Sep-2014 Python-Version: 3.5 Post-History: 16-Jan-20

peps.python.org

 

'CS🏅 > 파이썬 🖤' 카테고리의 다른 글

[파이썬 - 알쓸신잡] 왜 Dict는 List 보다 빠를까?  (0) 2023.06.12
[CS]CPU bound vs. I/O bound  (0) 2023.06.08
[Python] Typing 1편  (0) 2023.05.30
[Python] Iterator vs. Generator  (0) 2023.03.14
[Python] 파이썬 데코레이터  (0) 2022.12.27