Consider the following :
from typing import Tuple
class A(object):
def __init__(self):
pass
class B(A):
def __init__(self):
super(B, self).__init__()
def foo() -> Tuple[A]:
return tuple([B()])
foo()
PyCharm gives a warning:
Expected type 'Tuple[A]', got 'Tuple[B, ...]' instead
on tuple([B()]).
Since B "is a" A, this is annoying and wrong.
How to best deal with this?
CodePudding user response:
Look at your return statement:
return tuple([B()])
Let's unpack that a bit.
B() has type B, of course.
[B()] is a list, with a single element of type B. The static type of this list is deduced as list[B], a list of B instances.
Now look at tuple([B()]). You're calling tuple with a single argument of static type list[B]. If you call tuple with an argument of static type list[B], the static type of the result is tuple[B, ...].
It's not tuple[A], or even tuple[B]. The static type of [B()] doesn't contain any length information, so the type of the tuple doesn't contain any length information either. It's just a tuple containing some number of B instances.
The problem is, you went through a list. You need to not do that. Make a tuple directly:
return (B(),)
Then static analyzers can directly see that this is a tuple with a single element of type B, in a context where a tuple[A] is expected, and deduce that the tuple can and should be treated as a tuple[A].
CodePudding user response:
How to best deal with this?
The easiest way is returning a literal declaration instead of using a constructor (the syntax is also more concise), if you write the return as:
def foo() -> Tuple[A]:
return B(),
But you can also use the constructor provided it's initialized from a single element fixed length collection, the PyCharm linter won't complaint, for example:
def foo() -> Tuple[A]:
return tuple((B(),))
Looking carefully at the error message:
got 'Tuple[B, ...]'
The ellipsis ... here implies you are returning an arbitrary length tuple (the static type checker infers this because you initialize the tuple using a list), while the type hint -> Tuple[A] requires a fixed length single element tuple, see:
- Tuple, used by listing the element types, for example
Tuple[int, int, str]. The empty tuple can be typed asTuple[()]. Arbitrary-length homogeneous tuples can be expressed using one type and ellipsis, for exampleTuple[int, ...]. (The...here are part of the syntax, a literal ellipsis.)
I tested this both with Python 3.8 and Python 3.9 (notice that's the version when typing.Tuple was deprecated in favor of using parametrized builtins for type hints.)
