Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Type alias with union

I currently have this type alias, and some associated functions in my code:

Constant = int

def operation(data: Union[Constant, OtherTypes]):
    if isinstance(data, Constant):
        # do something
    else:
        # do something else

Now, I would like for Constant to also represent another type, say float. This Constant alias is used throughout my codebase, so I'd like to not have to change it everywhere.

I have tried:

Constant = (int, float)

This works nicely with isinstance, but the Unions complain that "TypeError: Union[arg, ...]: each arg must be a type."

I have then tried:

Constant = Union[int, float]

Now, the issues come with the isinstance; I get "TypeError: Subscripted generics cannot be used with class and instance checks".

Is there a way to do what I am trying to achieve ?

Thanks.

like image 588
Pankkake Avatar asked Sep 06 '25 03:09

Pankkake


2 Answers

As mentioned by Pankkake in their answer, for Python 3.10 you can simply do Constant = int | float and it will work everywhere.

However, if you must support older versions of Python, you can use the solutions provided in How to check a variable against Union type during runtime? by Frank, MSeifert and Richard Xia.

Python 3.10+

Constant = int | float

def operation(data: Constant | OtherTypes):
    if isinstance(data, Constant):
        # do something
    else:
        # do something else

Python 3.8+

Use the typing.get_args(tp) function to get a tuple with the union types, which you can use inside isinstance:

from typing import Union, get_args

Constant = Union[int, float]

def operation(data: Union[Constant, OtherTypes]):
    if isinstance(data, get_args(Constant)):
        # do something
    else:
        # do something else

get_args only returns the type's arguments without validating if the type is an Union or other generic type, which seems enough for your requirement.

If for some reason you also need to check at runtime if the Constant type is an Union specifically, use the typing.get_origin(tp) function:

from typing import Union, get_origin

if get_origin(Constant) is Union:
    # do something

Python 3.5.3+

Before 3.8 the get_args and get_origin functions didn't exist, so you needed to use the undocumented attributes __args__ and __origin__ instead.

def operation(data: Union[Constant, OtherTypes]):
    if isinstance(data, Constant.__args__):
        # do something
    else:
        # do something else

This still works for 3.10.5 but, since these attributes are undocumented, the snippet above could stop working with no short notice in any future Python version.

Python 3.5.0 to 3.5.2

Type hints were implemented in Python's 3.5.0 version. Up to 3.5.2 the attribute name to get an union's arguments was __union_params__:

def operation(data: Union[Constant, OtherTypes]):
    if isinstance(data, Constant.__union_params__):
        # do something
    else:
        # do something else

Of course, this attribute only exists for Union types, so if you need to check if a type is an Union, check for the existence of the attribute.

Note that this only works up to Python 3.5.2, since in 3.5.3 they changed the attribute name to __args__.

like image 55
George Bezerra Avatar answered Sep 07 '25 20:09

George Bezerra


isinstance supporting Unions comes with python 3.10 . As such, starting from that version, the second solution will work.

See https://peps.python.org/pep-0604/ .

like image 33
Pankkake Avatar answered Sep 07 '25 21:09

Pankkake