8000 [question] Overload with return type depending on an optional argument · Issue #5621 · python/mypy · GitHub
[go: up one dir, main page]

Skip to content

[question] Overload with return type depending on an optional argument #5621

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
zero323 opened this issue Sep 15, 2018 · 4 comments
Closed

Comments

@zero323
Copy link
zero323 commented Sep 15, 2018

i am working on annotation for a 3rd package. For simplicity let's say we have a function like this one:

def foo(x=0, f=None):
    return f(x) if f else x + 1

I would like to express, that the return type of the function is

  • int if f is missing.
  • The same as f return type otherwise.

So conceptually I would like to see something like this:

from typing import Callable, TypeVar, overload

T = TypeVar("T")

@overload
def foo(x: int = ..., f: Callable[[int], T]) -> T: ...
@overload
def foo(x: int = ...) -> int: ...

Of course, it is not valid, as non-default argument follows default argument. Similarly (as imprecise as it would be) we cannot use the following

from typing import Callable, TypeVar
from typing import overload

T = TypeVar("T")

@overload
def foo(x: int = ..., f: Callable[[int], T] = ...) -> T: ...
@overload
def foo(x: int = ...) -> int: ...

because the optional argument, makes the first signature broader than the second one.

A workaround is to split the first signature into two separate cases:

from typing import Callable, TypeVar
from typing import overload

T = TypeVar("T")

@overload
def foo(x: int, f: Callable[[int], T] = ...) -> T: ...
@overload
def foo(f: Callable[[int], T] = ...) -> T: ...
@overload
def foo(x: int = ...) -> int: ...

but it doesn't scale when the number of preceding optional arguments grows:

def bar(x=0,  y=0, z=0, f=None):
     return f(x + y + z) if f else x + y + z + 1

Is there any other solution here?

@ilevkivskyi
Copy link
Member

Is there any other solution here?

I think no. If it would be your code I would just recommend to move f to the first position. Unfortunately some existing APIs were designed without any thinking about types and are now hard to change (backwards compatibility etc.)

In certain cases it is possible to use *args, but it looks like it will be imprecise in this situation.

@Michael0x2a
Copy link
Collaborator

One option might be to use three overloads instead of two:

from typing import Callable, TypeVar, overload

T = TypeVar("T")

@overload
def foo(f: Callable[[int], T]) -> T: ...
@overload
def foo(x: int, f: Callable[[int], T]) -> T: ...
@overload
def foo(x: int = 0) -> int: ...
def foo(x, f):
    pass

reveal_type(foo(3))                     # int
reveal_type(foo(3, lambda x: "asdf"))   # str
reveal_type(foo(lambda x: "asdf"))      # str

In short, you split up the overload variant handling the callable into two -- one to handle the case where x is missing and the other for when x is present.

@zero323
Copy link
Author
zero323 commented Sep 15, 2018

@ilevkivskyi Than you for your prompt response. I was afraid this is the case.

@Michael0x2a I thought of that, but the number of required signatures grows exponentially in terms of the number of preceding optional args. In reality I deal with ~4 other args - feasible but hard to maintain.

@AlexWaygood
Copy link
Member

I'm closing this issue, as I think the question has been answered.

I'm sorry that the type system isn't as expressive as we'd like it to be in this case. If you'd like to make a feature request to expand the typing system, you should post in the https://github.com/python/typing issue tracker or the typing-sig mailing list.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants
0