Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to write type hints when you change type of a variable?

Say that I have

def foo(x: Union[str, list[str]], y: Union[str, list[str]]) -> tuple[list[str], list[str]]:
     
    x = cast(Union[list[str], tuple[list[str], ...]], str2list(x))  
    y = cast(Union[list[str], tuple[list[str], ...]], str2list(y))  
    
    return x,y
     
def str2list(*args: Union[str, list[str]]) -> Union[list[str], tuple[list[str], ...]]:
    vals = []
    for x in args:
        if not isinstance(x, list):
            x = [x]
        vals.append(x)
    if len(args) == 1:
        return vals[0]
    else:
        return tuple(vals)
    

x = 'My name is x'
y = ['First element', 'Second element']

z = str2list(x,y)

As you see, there are two cast calls in foo, but still mypy complains that

error: Incompatible types in assignment (expression has type "Union[List[str], Tuple[List[str], ...]]", variable has type "Union[str, List[str]]")  [assignment]

A workaround could be to define new variables and adjust the return type of foo as it follows

def foo(
    x: Union[str, list[str]], y: Union[str, list[str]]
) -> tuple[
    Union[list[str], tuple[list[str], ...]],
    Union[list[str], tuple[list[str], ...]],
]:

    xl = cast(Union[list[str], tuple[list[str], ...]], str2list(x))
    yl = cast(Union[list[str], tuple[list[str], ...]], str2list(y))

    return xl, yl

But I would prefer to overwrite x and y rather than defining two new variables xl and yl.

like image 360
Barzi2001 Avatar asked Jan 31 '26 06:01

Barzi2001


1 Answers

Here's a structured, augmented summary of the points made in the comments:

Key Issue

Mypy is a static type-checker, and redefining a variable with a value outside it's original type is a dynamic type operation, so mypy will complain about this.

Static Solutions

  1. One way to resolve this is to give new (potentially informative/descriptive) names to the new results:
def foo_1(
    x: Union[str, list[str]], y: Union[str, list[str]]
) -> tuple[list[str], list[str]]:
    cast_x = cast(Union[list[str], tuple[list[str], ...]], str2list(x))
    cast_y = cast(Union[list[str], tuple[list[str], ...]], str2list(y))

    return cast_x, cast_y
  1. Another way is to never give them names in the first place:
def foo_2(
    x: Union[str, list[str]], y: Union[str, list[str]]
) -> tuple[list[str], list[str]]:
    return cast(Union[list[str], tuple[list[str], ...]], str2list(x)), cast(
        Union[list[str], tuple[list[str], ...]], str2list(y)
    )

Silencing the Warning

Instead of bending to mypy's requirements, one can simply silence this specific type of [assignment] warning by running mypy with the --allow-redefinition flag. If you don't want to type this flag into the command line every time you run mypy, you can instead add the line allow_redefinition = True to your mypy configuration file.

like image 153
Alex Duchnowski Avatar answered Feb 01 '26 19:02

Alex Duchnowski