0

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.

metatoaster
  • 17,419
  • 5
  • 55
  • 66
Micah
  • 4,254
  • 8
  • 30
  • 38
  • @j1-lee same. BTW, I recommend @Micah to use `Tuple[int, ...]` or `Tuple[int, int, int]` rather than `tuple[int, int, int]` by `from typing import Tuple`. – hide1nbush Sep 27 '22 at 04:11
  • Python 3.10.5, using Tuple gets the same mypy error – Micah Sep 27 '22 at 04:13
  • weird. I got no error in 3.10.6. See https://codepaste.xyz/posts/yNMuXCiujJqobjQgMZcL – hide1nbush Sep 27 '22 at 04:19
  • Its mypy error, will this codepaste check it well? – Micah Sep 27 '22 at 04:21
  • 1
    @hide1nbush `b: Tuple[int, ...] = a((1, 2, 3))` is not the same as `b: tuple[int, int, int] = a((1, 2, 3))` – metatoaster Sep 27 '22 at 04:22
  • Does https://stackoverflow.com/questions/44287623/a-way-to-subclass-namedtuple-for-purposes-of-typechecking help? – Karl Knechtel Sep 27 '22 at 04:28
  • @metatoaster yes. But Micah seems still getting error using `Tuple` – hide1nbush Sep 27 '22 at 04:29
  • @hide1nbush its mypy error, I edited a example with sub List the same way, will not raise error. Can you help? – Micah Sep 27 '22 at 04:31
  • @hide1nbush To be clear `Tuple[int, ...]` will not result in an error, but explicit number of elements without the ellipsis will. I am pointing out how you have changed the code and thus you failed to reproduce the error with the correct, affected code. – metatoaster Sep 27 '22 at 04:33
  • @KarlKnechtel I cant find similarities, can you give me an easier example maybe? – Micah Sep 27 '22 at 04:33
  • I'm not familiar with Mypy; I quite like dynamic typing. That was my best attempt to find a related question by searching with appropriate terms. – Karl Knechtel Sep 27 '22 at 04:34
  • 1
    Regarding ability to preserve type parameters on subclass (like `a[int,int,str]`), i.e. variadic generics, see [discussion in comments to this question](https://stackoverflow.com/questions/73838487/how-to-pass-generic-through-type-alias). The proposed answer won't work here (since you need a subclass and not an alias), but PEP646 discussed there is your (only?) way to go, still lacking `mypy` support. – STerliakov Sep 27 '22 at 14:02

1 Answers1

1

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.

metatoaster
  • 17,419
  • 5
  • 55
  • 66
  • Yes, indeed this way works, if I want to become able to assign different number of generics dynamically like tuple itself. What should I do? – Micah Sep 27 '22 at 05:52
  • @Micah You basically have to override the `__new__` method for you `tuple` subclass with the intended signature (i.e. number of arguments), but then you will run into this [issue](https://github.com/python/mypy/issues/8957) that has not been fixed. – metatoaster Sep 27 '22 at 06:03
  • In his case, using tuple.__new__( cls , iterable ), can resolve the problem. But I think my question is more related to how can I assign dynamic number of generic. – Micah Sep 27 '22 at 06:08
  • this example can give us options to assign same type, but tuple can have different types, how can we do it? https://stackoverflow.com/questions/67529338/literal-as-part-of-generic-type-python-typing – Micah Sep 27 '22 at 06:11
  • I would just use the default `tuple[...]` signature and reserve subclasses for concrete one-off alias of some defined `tuple[...]`. Honestly, what you are asking for is currently not possible to be validated/expressed in Python/mypy. Try reporting what you want as an issue on the [mypy issue tracker](https://github.com/python/mypy/issues/). – metatoaster Sep 27 '22 at 06:15
  • Alternatively, rather than subclassing, compose a new class that has a specific `tuple` type signature for instances of that new class. – metatoaster Sep 27 '22 at 06:16