I'm trying to learn how generics in Python work.
I got an example from Fluent Python book. It works fine, but I'd like to know if it is possible to make pyright checker satisfied?
from typing import Protocol
class Repeatable[T](Protocol):
def __mul__(self: T, repeat_count: int) -> T:
...
def double[RT: Repeatable](x: RT) -> RT:
return x * 2
if __name__ == '__main__':
print(double(3)) # pyright error
print(double('A')) # pyright error
print(double([1, 2])) # pyright error
Thank You for Your help!
There are two issues:
T
is used as a self-type, but is bound to the type instance itself. This is a logical contradiction;T
cannot vary w.r.t. instances ofRepeatable
, since it is statically bound to precisely that instance. Instead, you could either usetyping.Self
, or moveT
to the__mul__
scope.__mul__
has a specific name, and can be used as either a positional- or keyword-argument. So because it can be used as keyword arg, the name matters., i.e.obj.__mul__(repeat_count=...)
is different fromobj.__mul__(birds_arent_real=...)
. Again, there are two possible solutions here; make it positional only (i.e. by placing a/
after it), or "remove" the name (i.e. by prefixing it with__
).To illustrate, this is one of the possible solutions
See for yourself on the pyright playground. It even works in pyright strict mode (and in expect mode it gets 5 stars for "through the fire and the flames" while speedrunning rainbow road).