Skip to content

Instantly share code, notes, and snippets.

@xenophonf
Last active November 4, 2025 04:43
Show Gist options
  • Select an option

  • Save xenophonf/05d40c18f2e16a238b1e045cb02f2c90 to your computer and use it in GitHub Desktop.

Select an option

Save xenophonf/05d40c18f2e16a238b1e045cb02f2c90 to your computer and use it in GitHub Desktop.
On using small classes with Python match-case statements
19:49 < dcb> so, I'm looking for opinions on a pattern I often end up using with python that I borrowed from erlang and haskell. The gist is to wrap some object inside a
small class just so I can use it in a match-case statement. Here's a snippet should give you an idea: https://bpa.st/C6LQ
19:50 < dcb> others often react in one of two ways: (╭ರ_•́) or ¯\_(ツ)_/¯
20:00 < phy1729> IMO Python's typing isn't algebraic enough for that to feel natural. Though TIL __match_args__ so maybe that'll change in my head.
20:03 < phy1729> dcb: though I'd be inclined to make Field generic https://mypy-play.net/?mypy=latest&python=3.12&gist=b4aa6967a03c0ac147130af73f8942db
20:17 < dcb> XenophonF: these patterns often end up appearing when I'm debugging branching logic early on development. Good call for making the base generic. And
`__match_args__` is something I've started to use quite a bit recently
20:17 < dcb> phy1729 ^
# approach 1: pattern-matching sub-classes
from typing import Any
from abc import ABC
# the generic, parent class
class Field(ABC):
__match_args__ = ('data',)
def __init__(self, data: Any):
self.data = data
def __repr__(self) -> str:
return f'{self.__class__.__name__}(data={self.data!r})'
def __str__(self) -> str:
return str(self.data)
# the more specific, concrete classes
class UID(Field):
def __init__(self, data: int):
self.data = data
class Name(Field):
def __init__(self, data: str):
self.data = data
class Email(Field):
def __init__(self, data: str):
self.data = data
def find_someone() -> tuple[UID, Name, Email]:
return (UID(123), Name('Jimmy'), Email('[email protected]'))
def do_something_with(field: Field):
match field:
case UID(data=uid):
print(f'* field {field!r} matches an uid: {uid}')
case Name(data=name):
print(f'* field {field!r} matches a name: {name}')
case Email(data=email):
print(f'* field {field!r} matches an email: {email}')
case _:
print(f'* failed to match field {field!r} with existing patterns!')
(uid, name, email) = find_someone()
do_something_with(uid)
do_something_with(name)
do_something_with(email)
# approach 2: using enums as atoms
from enum import StrEnum, auto
from typing import Any
class Field(StrEnum):
UID = auto()
Name = auto()
Email = auto()
type UID = int
type Name = str
type Email = str
def find_someone() -> tuple[UID, Name, Email]:
return (123, 'Jimmy', '[email protected]')
def do_something_with(data: tuple[Field, Any]):
match data:
case (Field.UID, uid):
print(f'* data {data} matches an uid: {uid}')
case (Field.Name, name):
print(f'* data {data} matches a name: {name}')
case (Field.Email, email):
print(f'* data {data} matches an email: {email}')
case _:
print(f"* failed to match data {data!r} with existing patterns!")
(uid, name, email) = find_someone()
do_something_with((Field.UID, uid))
do_something_with((Field.Name, name))
do_something_with((Field.Email, email))
from abc import ABC
# the generic, parent class
class Field[T](ABC):
__match_args__ = ('data',)
def __init__(self, data: T):
self.data = data
def __repr__(self) -> str:
return f'{self.__class__.__name__}(data={self.data!r})'
def __str__(self) -> str:
return str(self.data)
# the more specific, concrete classes
class UID(Field[int]):
pass
class Name(Field[str]):
pass
class Email(Field[str]):
pass
def find_someone() -> tuple[UID, Name, Email]:
return (UID(123), Name('Jimmy'), Email('[email protected]'))
def do_something_with(field: Field):
match field:
case UID(data=uid):
print(f'* field {field!r} matches an uid: {uid}')
case Name(data=name):
print(f'* field {field!r} matches a name: {name}')
case Email(data=email):
print(f'* field {field!r} matches an email: {email}')
case _:
print(f'* failed to match field {field!r} with existing patterns!')
(uid, name, email) = find_someone()
do_something_with(uid)
do_something_with(name)
do_something_with(email)

Comments are disabled for this gist.