https://stackoverflow.com/a/47784683
이런 식으로 구조체를 표현하고자 하는 시도들이 여러가지가 있어왔는데, 결국에는 파이썬의 한계이자 단점으로 볼 수 있을 것 같습니다. ADT(algebraic data type)를 학교 수업때 ocaml로 처음 접했었는데 일할때는 이런 식으로 흉내만 내야 한다는 게 좀 안타깝기도 하네요
ilotoki님께서 만드신 라이브러리가 가장 ADT에 근접한 사례로 볼 수 있을 것 같습니다. 언젠가 표준 라이브러리에 포함되고 널리 쓰이게 된다면 좋을 것 같습니다
Message
의 구현은 Union으로 하게 된다면 메서드 상속을 이용할 수 없습니다. 예를 들어
from fieldenum import fieldenum, Variant, Unit
@fieldenum
class Message:
Quit = Unit
Move = Variant(x=int, y=int)
Write = Variant(str)
ChangeColor = Variant(int, int, int)
def process(self):
...
위와 같이 .process
메서드를 추가하면 모든 배리언트들에 대해 .process()
메서드를 사용할 수 있습니다.
# Message.process() 메서드를 각 배리언트에서 사용 가능
Message.Quit.process()
Message.Move(x=123, y=456).process()
Message.Write("hello, world").process()
Message.ChangeColor(123, 000, 89).process()
또한 제가 설명드린 repr는 '해당 enum의 배리언트로서의 repr'를 의미한 것입니다.
예를 들어 fieldenum을 repr를 감싸 호출하면 다음과 같이 실행됩니다.
print(repr(Message.Move(x=123, y=456))) # Message.Move(x=123, y=456)
커스텀 __repr__
가 없으면 Message
enum의 하위 배리언트라는 사실이 표현되지 않습니다.
Quit
은 유닛 배리언트로 호출 없이 사용합니다.
Message.Quit # 별도의 호출 (예: `Message.Quit()`) 없이 사용 가능
또한 호출을 사용해야 하는 배리언트 종류인 fieldless 배리언트의 경우에는 싱글톤으로서 is
연산자로 확인할 수 있습니다.
from fieldenum import fieldenum, Variant, Unit
class WithFieldless:
Fieldless = Variant()
assert WithFieldless.Fieldless() is WithFieldless.Fieldless()
fieldenum을 사용하면 이렇게 놓치기 쉬운 다양한 구현 디테일을 자동으로 챙기는 데에 도움이 됩니다.
그럼에도 불구하고 class 정의에 필요한 보일러플레이트 코드가 없다는 점, enum과 class를 한가지 인터페이스로 사용할 수 있는 점이 장점이 될 수 있겠네요. 상세한 설명 감사합니다