Reputation: 4550
I found out its able to subclass in this way or through throw __new__
with no problem, but type checking.
class a(tuple):
pass
print(a((1, 2, 3))) # <---- ( 1, 2, 3 )
b: tuple[int, int, int] = a((1, 2, 3)) # <--- Error
# Incompatible types in assignment (expression has type "a", variable has type "Tuple[int, int, int]")
c: tuple = a((1, 2, 3)) # <--- Ok
d: tuple[int, int, int] = (1, 2, 3) # <--- Ok
The same way when you subclass list will work.
class a( list[ T ] )
def __init__(self, * pax : T ) : pass
b : list[ int ] = a( 1, 2 ) # <--- Ok
c = a[ int ]( 1, 2 ) # <--- Ok
How can I subclass tuple correctly? thank you for your advise.
Upvotes: 0
Views: 291
Reputation: 18908
The issue you are being confounded by has nothing to do with subclassing but has everything due to how mypy assumes any sequence passed as an argument to any tuple
and its subclasses by their name (e.g. tuple((item, item, ..., item))
, as opposed to the Python tuple syntax (item, item, ..., item,)
) to have the signature Tuple[Any, ...]
(as that is the type signature for the default tuple
constructor). Consider the following code example:
one_tuple: tuple[int] = tuple((1,))
answer: tuple[int] = (1,)
print(one_tuple == answer)
Running the above code with python
will produce the output True
, which is what is expected. However, mypy
will produce the following error message:
onetuple.py:1: error: Incompatible types in assignment (expression has type "Tuple[int, ...]", variable has type "Tuple[int]")
Found 1 error in 1 file (checked 1 source file)
Given that it is impossible to use Python's standard tuple syntax to produce instances of its subclass (as it always produce a tuple
), any subclasses of tuple
(e.g. class MyTuple(tuple): ...
) would then therefore not be able to satisfy any Tuple[T]
where T
is not a sequence of variable length.
While the following assertion is not stated in the question, if you know for sure your tuple
subclass is going to have some finite length, this may be a suitable workaround:
class MyTuple(tuple[int, int, int]):
pass
mytuple: tuple[int, int, int] = MyTuple((1, 2, 3))
In this case, mypy
will not generate any validation errors.
As an addendum, with the introduction of PEP 646 - Variadic Generics, this may become possible starting from Python 3.11, however mypy
has no support for this quite yet (see GitHub issue python/mypy#12840, under new typing features, PEP 646 - as of 2022-10-14 no sub-issue has been tracked there yet), and pyright
and pyre
doesn't appear to correctly check the statement one_tuple: tuple[int] = tuple((1, 2,))
as invalid, thus I end my further effort here for now given that I am unsure if it does actually correctly support PEP 646.
Upvotes: 1