In python multimethod, how to define a case for empty list?

65 Views Asked by At

For example, I define

@multimethod
def f(x:list[str]):
    return 1
@multimethod
def f(x:list[int]):
    return 0

then

f([])

gives error

DispatchError: ('f: 2 methods found', (<class 'multimethod.<class 'list'>'>,), [(<class 'multimethod.list[str]'>,), (<class 'multimethod.list[int]'>,)])

how to define a case specific for empty list?

1

There are 1 best solutions below

2
35308 On

Given that you're checking some property of your list you should use @overload instead of @multimethod that way you can supply some custom predicate. Note as per the documentation "Overloads dispatch on annotated predicates. Each predicate is checked in the reverse order of registration" so be carefull with the order. Anyways we can define some simple function isinstance(lst, list) and not lst and use lambda notation to write:

from multimethod import overload
@overload
def f(lst: list[str]):
    return 1
@overload
def f(lst: list[int]):
    return 0
@overload
def f(lst: lambda lst: isinstance(lst, list) and not lst): # the not lst checks that it has elements
    return 2
print(f(["1"])) # returns 1
print(f([1])) # returns 0
print(f([])) # returns 2

And it all seems to work as expected. If you have issues with the isinstance you can also use multimethod's built in isa function like this:

from multimethod import isa, overload
@overload
def f(lst: list[str]):
    return 1
@overload
def f(lst: list[int]):
    return 0
@overload
def f(lst: lambda lst:isa(list)(lst) and not lst): # the not lst checks that it has elements
    return 2
print(f(["1"])) # returns 1
print(f([1])) # returns 0
print(f([])) # returns 2