How to ensure a generic function only accepts args of the same type
Hi typing-sig, This question has come up a few times from pytype's users: How do you write a generic function that only takes arguments that are the same subclass of a particular base class? That is, if you have a function like: def do_stuff(x, y): ... If you want it to take two args of the same type, but that type is otherwise not constrained, use a regular TypeVar: T = TypeVar("T") def do_stuff(x: T, y: T): ... If you want do_stuff to only accept args of certain types, you can limit the TypeVar to those types: T = TypeVar("T", int, str) def do_stuff(x: T, y: T): ... do_stuff(<int>, <int>) or do_stuff(<str>, <str>) are the only valid calls. This works if the constraining types are known ahead of time. If you want do_stuff to only accept args of a particular base type, use bound: T = TypeVar("T", bound=Base) def do_stuff(x: T, y: T): ... If A and B are subclasses of Base, then do_stuff(A(), A()), do_stuff(A(), B()) and do_stuff(A(), Base()) are all legal. As far as I can tell, there is no way to express that do_stuff(A(), A()) and do_stuff(B(), B()) are legal while do_stuff(A(), B()) and do_stuff(A(), Base()) are illegal, for all subclasses of Base. Am I missing something? (Pytype does accept one way to do it, though this is unintended: T = TypeVar("T", Base, Base) def do_stuff(x: T, y: T): ... mypy treats this the same as the bound=Base case, but pytype will complain about do_stuff(A(), B()). PEP 484 doesn't have anything to say about this case, as far as I know.) -- Teddy
I can't think of any way to prevent the constraint solver from attempting to widen the type to accommodate both `A` and `B` other than to explicitly constrain it (`T = TypeVar("T", A, B)`). -- Eric Traut Contributor to Pyright & Pylance Microsoft Corp.
T = TypeVar("T", bound=Base) def do_stuff(x: T, y: T): ...
If A and B are subclasses of Base, then do_stuff(A(), A()), do_stuff(A(), B()) and do_stuff(A(), Base()) are all legal.
So I'm not a user of pytype, but that doesn't look like it should pass to me. I'd have thought what you've written there should correspond to the behaviour you're after -- of admitting A, A or B, B but not A, Base -- and the following should correspond to additionally allowing A, Base: T = TypeVar("T", bound=Base) S = TypeVar("S", bound=Base) def do_stuff(x: T, y: S): ...
participants (3)
-
Eric Traut
-
Patrick Kidger
-
Teddy Sudol