pythonpython-typingmypypython-3.9

Incompatible type with element of Union


I recently received this good answer employed in the following:

from typing import Union, cast


class Default:
    """Placeholder for default arguments."""


# ham[] is mutable.  `None` has meaning (or is not preferred).
def spam(ham: Union[list[str], None, type[Default]] = Default):
    if ham is Default:
        ham = ['prosciutto', 'jamon']
    #ham = cast(Union[list[str], None], ham)
    #assert isinstance(ham, (list, type(None)))
    if ham is None:
        print('Eggs?')
    else:
        print(str(len(ham)) + ' ham(s).')

mypy error:

Failed (exit code: 1) (2655 ms)

main.py:17: error: Argument 1 to "len" has incompatible type "Union[List[str], Type[Default]]"; expected "Sized"
Found 1 error in 1 file (checked 1 source file)

To avoid similar mypy errors, I've always used one of the two idioms commented out.

Are there other solutions?


Solution

  • Why not use an instance of the Default class instead of the class itself? MyPy understands isinstance and can infer the type of objects used in it, in contrast to type-narrowing that uses is.

    As for using an instance of a class as a default value for a parameter: according to the function definition section of the Python documentation, there is no significant overhead and it can be neglected:

    Default parameter values are evaluated from left to right when the function definition is executed. This means that the expression is evaluated once, when the function is defined, and that the same “pre-computed” value is used for each call.


    For example:

    from typing import Union, cast
    
    
    class Default:
        """Placeholder for default arguments."""
    
    
    # ham[] is mutable.  `None` has meaning (or is not preferred).
    def spam(ham: Union[list[str], None, Default] = Default()):
        if isinstance(ham, Default):
            ham = ['prosciutto', 'jamon']
        if ham is None:
            print('Eggs?')
        else:
            print(str(len(ham)) + ' ham(s).')