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.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
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