python/응용

functools.singledispatch 로 typeclass 흉내 내기

wefree 2024. 1. 24. 18:10

 

 

코드

import dataclasses
import functools
import math
from typing import Any


@dataclasses.dataclass
class Circle:
    radius: float


@dataclasses.dataclass
class Rectangle:
    width: float
    height: float


@functools.singledispatch
def area(shape: Any) -> float:
    raise TypeError(f"{type(shape)} is not allowed")


@area.register
def _(shape: Circle) -> float:
    return math.pi * shape.radius ** 2


@area.register
def _(shape: Rectangle) -> float:
    return shape.width * shape.height


result = area(Circle(radius=2))
print(result)

 

singledispatch 한계

https://dev.to/wemake-services/typeclasses-in-python-3ma6

 

생각해 볼 문제

Overloading

python 에서 아래처럼 구현이 허용되면 좋겠지만, 기본적으로 type 으로 function 을 구분할 수 없으므로 불가능하다.

def area(shape: Circle) -> float:
    return math.pi * shape.radius ** 2


def area(shape: Rectangle) -> float:
    return shape.width * shape.height

 

하지만 https://github.com/mrocklin/multipledispatch/ 를 이용하면 비슷하게 코딩할 수 있어 보이기도 하다.

(사용해 보니 약간 불안정해 보임 - Circle 과 Retangle member 를 혼용?)

from multipledispatch import dispatch


@dispatch(Circle)
def area(shape) -> float:
    return math.pi * shape.radius ** 2


@dispatch(Rectangle)
def area(shape) -> float:
    return shape.width * shape.height

 

 

typeclass 흉내를 냈지만

singledispatch 를 사용한 코드는, 결국 아래랑 다를게 없어 보인다.

def area(shape: Any) -> float:
    if isinstance(shape, Circle):
        return math.pi * shape.radius ** 2
    elif isinstance(shape, Rectangle):
        return shape.width * shape.height
    else:
        raise TypeError(f"{type(shape)} is not allowed")

 

참고

  • 아래처럼 singledispatch 에서 Union type 을 사용할 수 있는데, python 3.11 이상 부터 가능한 듯
@area.register
def _(shape: Circle | Rectangle) -> float:
    ...