Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pythonic way to handle arguments with values in Union

In the code below, print_pos accepts one argument which can be of three different types.

from typing import List, Tuple, Union

pos_t = Tuple[int, int]
anchor_t = Tuple[str, str]
anchor_pos_t = Tuple[anchor_t, pos_t]

def print_pos(
        pos: Union[
            pos_t,
            anchor_pos_t,
            List[Union[pos_t, anchor_pos_t]]
        ]
) -> None:
    if isinstance(pos, tuple) and isinstance(pos[0], int):
        print('xy =', pos)
    elif isinstance(pos, tuple) and isinstance(pos[0], tuple):
        print('anchor =', pos[0])
        print('xy =', pos[1])
    elif isinstance(pos, list):
        print('[')
        for p in pos:
            print_pos(p)
        print(']')
    else:
        raise ValueError('invalid pos')

print_pos((0, 100))
print_pos((('right', 'bottom'), (0, 100)))
print_pos([
    (0, 100),
    (('right', 'bottom'), (0, 100))
])

Right now, I use isinstance to check for the different possibilities for the type of pos but I find the code rather clumsy. Is there a more convenient/elegant way to do that? In particular is there a mean to reuse types I defined (pos_t, anchor_t, anchor_pos_t) in my type check?

like image 510
qouify Avatar asked Feb 13 '26 23:02

qouify


1 Answers

You can use the typeguard library to check variable types at runtime.

This library is mainly for runtime type validation, rather than conditional type checking, so an extra is_type function needs to be defined to fit your needs.

Extra type casts are also unfortunately necessary to prevent type checker errors.

from typing import Any, List, Tuple, Union, cast

from typeguard import check_type


pos_t = Tuple[int, int]
anchor_t = Tuple[str, str]
anchor_pos_t = Tuple[anchor_t, pos_t]


def is_type(value: Any, expected_type: Any) -> bool:
    """
    Return whether the given value is of the expected type or not.
    """
    try:
        check_type('<blank>', value, expected_type)
        return True
    except TypeError:
        return False


def print_pos(
    pos: Union[pos_t, anchor_pos_t, List[Union[pos_t, anchor_pos_t]]]
) -> None:
    if is_type(pos, pos_t):
        pos = cast(pos_t, pos)
        print('xy =', pos)
    elif is_type(pos, anchor_pos_t):
        pos = cast(anchor_pos_t, pos)
        print('anchor =', pos[0])
        print('xy =', pos[1])
    elif is_type(pos, List[Union[pos_t, anchor_pos_t]]):
        pos = cast(List[Union[pos_t, anchor_pos_t]], pos)
        print('[')
        for p in pos:
            print_pos(p)
        print(']')
    else:
        raise ValueError('invalid pos')


print_pos((0, 100))
print_pos((('right', 'bottom'), (0, 100)))
print_pos([(0, 100), (('right', 'bottom'), (0, 100))])

This isn't the cleanest solution, but it works.

I would recommend using a more object-oriented approach with classes if possible, to eliminate the need for union types.

like image 195
KYDronePilot Avatar answered Feb 16 '26 15:02

KYDronePilot



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!