Replies: 3 comments
-
I think I understand your use case, but a parameterized class Parent[T]:
def method(self) -> Self:
return self
class Child(Parent[int]):
pass
Child().method() In this example, It would also require that there's a way to construct a new I think the use case you're describing would require a generalized higher-kinded type (HKT) mechanism. This has been discussed previously, but discussions haven't progressed much. HKT's would add quite a bit of complexity. They're not supported in most languages that support generics. |
Beta Was this translation helpful? Give feedback.
-
This would also resolve my use case described here: https://discuss.python.org/t/41483, and similar to @dvarrazzo, my only alternative is to redefine the overloads in each subclass. In my opinion @erictraut raised valid concerns; I think this feature would only make sense in a very specific case:
Code sample in pyright playground from typing import Self, reveal_type
class Parent[T]:
# Valid
@classmethod
def method(cls, a: type[T]) -> Self[T]:
...
# Invalid
@classmethod
def method2(cls) -> Self[str]:
...
class Child(Parent[int]):
pass
reveal_type(Parent.method(int)) # pyright seems to handle it already, although probably not on purpose
reveal_type(Child.method(int)) # If another type is being used (e.g. `str`), the type checker will already complain as the inferred signature of `method` is method(a: type[int]) -> Child But even then, I'm pretty sure I'm missing a lot of cases that could lead to undefined behavior (e.g. as said above when a subclass has an extra type var). |
Beta Was this translation helpful? Give feedback.
-
It seems that the right solution is to use the generic default as per PEP 696. It seems to work, both in the synthetic example provided in #1556 and in psycopg. |
Beta Was this translation helpful? Give feedback.
-
This question comes from psycopg/psycopg#308 and from the outdated #1197, and is more focused on only one of the aspects previously asked about. (The other is asked about in #1556).
I understand from PEP 673 that Self is doesn't accept a parameter:
Follows a pretty contrived example of an ambiguous situation. However, if the context is not ambiguous, I think that there are valid use cases of a parametric self. One example is with a generic class, where there is a meaningful default for the type. For instance, in psycopg, the
connect()
factory function creates an object that return tuples, as per DBAPI spec, but it can take an optionalrow_factory
parameter to specify which Python objects to return the data as. A simplified version is:Running mypy 1.8:
The desired outcome is that
connect()
would instead return aConnection[TupleRow]
, but the info is clearly not there. What I would change here is that I should be able to define:for the overload variant without a default, and the parameters passed to Self would indicate the parameters for the generic types.
Wouldn't this be a reasonable use for Self parametrization?
Note:
Connection[TupleRow].connect()
, but this is non backward-compatible and I may as well force people to useConnection.connect(row_factory=tuple_row)
.Connection[TupleRow]
, this is what we currently do, but this is the defeat ofSelf
, and the main shortcoming is that it becomes horrible when the object must be subclassed, because complex signatures must be repeated just to change the return type annotations. Example: none of this should be necessary.Beta Was this translation helpful? Give feedback.
All reactions