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.)