Annotating `self` with a protocol?

I have noticed that mypy and pyright have this (undocumented?) feature where you can annotate `self` in a method with a protocol:
from dataclasses import dataclass from typing import Protocol
class HasX(Protocol): x: int
class Base: def double_x(self: HasX) -> int: return 2 * self.x
@dataclass class ChildCorrect(Base): x: int ChildCorrect(3).double_x() # OK
@dataclass class ChildIncorrect(Base): y: int ChildIncorrect(3).double_x() # err: not compatible with HasX
Note that `HasX` isn't anywhere in the base classes of `Base`.
My first question is whether this feature is documented somewhere.
And my second question is whether this could also take into account methods defined in the actual class. So, for example, if I change the above example to
class HasX(Protocol): x: int
class Base: def z(self) -> int: return 4 def double_x_plus_z(self: HasX) -> int: return 2 * self.x + self.z() # type-check error
then the type checker complains about `self.z()` because `HasX` doesn't have this method. However, it seems to me that any possible type for `self` will have this method, because `Base` defines it right there. So, shouldn't this type-check?
Cheers, Thomas

I don't think the ability to annotate the `self` parameter is documented, but its behavior falls out naturally from the ability to annotate any other function parameter. The only thing that makes `self` special is that the corresponding argument is typically supplied through a binding operation (where an object instance is bound to the parameter as part of a method access) rather than passed directly via a call expression argument.
Once you declare `self` as type `HasX`, the type checker will enforce that type. So it's correct for a type checker to emit an error in your `double_x_plus_z` example above.
It's not safe to assume that `self` also has the type `Self` because an instance method can be invoked on the class directly, and the caller can pass an argument for `self`. For example, `Base.double_x_plus_z(has_x_instance)`.
It sounds like what you want is an intersection type — a way to declare that `self` is both type `HasX` and `Self`. There is some work underway to determine whether it's feasible to add intersection types to the type system.
-- Eric Traut Contributor to Pyright & Pylance Microsoft

Mypy has documentation for this under "advanced uses of self types".
https://mypy.readthedocs.io/en/stable/more_types.html#advanced-uses-of-self-...
Den tis 22 nov. 2022 16:41Eric Traut eric@traut.com skrev:
I don't think the ability to annotate the `self` parameter is documented, but its behavior falls out naturally from the ability to annotate any other function parameter. The only thing that makes `self` special is that the corresponding argument is typically supplied through a binding operation (where an object instance is bound to the parameter as part of a method access) rather than passed directly via a call expression argument.
Once you declare `self` as type `HasX`, the type checker will enforce that type. So it's correct for a type checker to emit an error in your `double_x_plus_z` example above.
It's not safe to assume that `self` also has the type `Self` because an instance method can be invoked on the class directly, and the caller can pass an argument for `self`. For example, `Base.double_x_plus_z(has_x_instance)`.
It sounds like what you want is an intersection type — a way to declare that `self` is both type `HasX` and `Self`. There is some work underway to determine whether it's feasible to add intersection types to the type system.
-- Eric Traut Contributor to Pyright & Pylance Microsoft _______________________________________________ Typing-sig mailing list -- typing-sig@python.org To unsubscribe send an email to typing-sig-leave@python.org https://mail.python.org/mailman3/lists/typing-sig.python.org/ Member address: git@antonagestam.se
participants (3)
-
Anton Agestam
-
Eric Traut
-
Thomas Kehrenberg