Skip to content

Self types in generic classes  #2354

Closed
Closed

Description

@JukkaL

Do something reasonable with self types (e.g., self: T) in generic classes.

This is continuation of work in #2193.

Activity

  1. added this to the milestone on Oct 27, 2016
  2. jboning commented on Dec 8, 2016

    @jboning
    Contributor

    A motivating example is typeshed's AbstractSet:

    class AbstractSet(Sized, Iterable[_T_co], Container[_T_co], Generic[_T_co]):
        def __and__(self, s: AbstractSet[Any]) -> AbstractSet[_T_co]: ...
        def __or__(self, s: AbstractSet[_T]) -> AbstractSet[Union[_T_co, _T]]: ...
    

    With #2193 we can annotate __and__, and it works beautifully:

        def __and__(self: SelfT, s: AbstractSet[Any]) -> SelfT: ...
    
    def f(s, fs):
        # type: (MutableSet[str], FrozenSet[str]) -> None
        reveal_type(s.__and__)  # Revealed type is 'def (typing.AbstractSet[Any]) -> typing.MutableSet[builtins.str]'
        reveal_type(fs.__and__)  # Revealed type is 'def (typing.AbstractSet[Any]) -> typing.FrozenSet[builtins.str]'
    

    But, we don't have the tools to give __or__ a similarly specific type. To do this, we need some way to talk about the "generic" part of the self type.

  3. removed this from the milestone on Mar 29, 2017
  4. elazarg commented on Apr 3, 2017

    @elazarg
    Contributor

    I think this issue is resolved - we can talk about the generic part if the self type. Or maybe I don't completely understand the issue.

  5. jboning commented on Apr 3, 2017

    @jboning
    Contributor

    What would the annotated version of AbstractSet's __or__ method be using self types? I think that's a good illustrative example.

  6. elazarg commented on Apr 3, 2017

    @elazarg
    Contributor

    I admit all these underscores confuse me, but maybe you wanted something like this?
    (I am writing AS instead of AbstractSet to make it easier for me to read :) )

    class AS(Generic[_T_co]):
        def __and__(self: AS[T1], s: AS[T2]) -> AS[T1]: ...
        def __or__ (self: AS[T1], s: AS[T2]) -> AS[Union[T1, T2]]: ...
  7. elazarg commented on Apr 3, 2017

    @elazarg
    Contributor

    (actually I think __and__ should return AS[Intersect[T1, T2]] if we had something like that)

  8. jboning commented on Apr 3, 2017

    @jboning
    Contributor

    Those annotations aren't as specific as I'd like--I want to be able to use self types to select the right return type depending on the subclass of AbstractSet. So FrozenSet.__and__ and FrozenSet.__or__ should return some kind of FrozenSet.

    I'm imagining something like this, except that I don't think it's possible to write SelfT[T1]:

    class AS(Generic[_T_co]):
        def __and__(self: SelfT[T1], s: AS[T2]) -> SelfT[T1]: ...
        def __or__ (self: SelfT[T1], s: AS[T2]) -> SelfT[Union[T1, T2]]: ...
    
  9. ilevkivskyi commented on Apr 3, 2017

    @ilevkivskyi
    Member

    SelfT[T1] is this higher order kind? :-)

  10. JukkaL commented on Apr 4, 2017

    @JukkaL
    CollaboratorAuthor

    @jboning Yes, that is what this issue was originally about.

    However, it's unclear if we'd want that annotation for AbstractSet. For example, consider this example:

    class C(set): pass
    print(type(C([1]) | C([2]))  # <class 'set'> (not C!)
    

    Arguably AbstractSet[T] already has the correct return type, and if some subclasses of AbstractSet return more specific types, they should be indicated in the stubs/annotations for those classes, not in AbstractSet. Maybe MutableSet should override methods such as __or__ to return MutableSet instead.

  11. jboning commented on Apr 5, 2017

    @jboning
    Contributor

    Huh. I agree that then it makes sense for MutableSet and FrozenSet to override with their own, more specific types for these methods. I'll go post a typeshed change for that.

    Is it still a useful example of the sort of annotation one might want to be able to write?

  12. ilevkivskyi commented on Aug 16, 2017

    @ilevkivskyi
    Member

    Raising priority since there were quite a few requests for this recently.

  13. JukkaL commented on Sep 18, 2017

    @JukkaL
    CollaboratorAuthor

    Decreasing priority back to normal since this seems like a somewhat complicated feature and requires an addition to mypy_extensions (and/or PEP 484).

  14. hauntsaninja commented on Jun 21, 2025

    @hauntsaninja
    Collaborator

    We have generic self types now. We don't have higher kinds, but that can be discussed elsewhere

  15. jab commented on Jun 22, 2025

    @jab
    Contributor

    We have generic self types now. We don't have higher kinds

    Just to clarify, it is still expected that we cannot yet change code like this

        def inverse(self) -> BidirectionalMapping[VT, KT]:

    to this:

        def inverse(self) -> typing.Self[VT, KT]:

    Is that right?

    (When I try this with mypy 1.16.1, I get error: Self type cannot have type arguments. However, with that change and the removal of hacks like this, calling reveal_type(foo.inverse) (where foo is an instance of the concrete subclass bidict) actually somewhat works: Revealed type is "bidict._bidict.bidict[builtins.int, builtins.str]" -- there the key type and value type are incorrect (they should be str and int, respectively -- but the subclass type is correct.)

    but that can be discussed elsewhere

    I searched the tracker for another issue to subscribe to now that this is closed, and only found #6066. Is that the one you recommend following?

    Thank you ❤️

  16. brianschubert commented on Jun 22, 2025

    @brianschubert
    Collaborator

    I think just

        def inverse(self) -> typing.Self: ...

    may be what you're looking for.

    If you’re interested in proper HKTs, then python/typing#548 would be a good issue to watch.

  17. jab commented on Jun 22, 2025

    @jab
    Contributor

    That doesn't work, as it discards the information that the key type and value type are swapped in the type returned by inverse, which is required for call sites to type check.

    Already watching python/typing#548, thanks for making sure. Wondering if there are any other issues in the mypy tracker for the outstanding follow-up work (like allowing Self to accept type arguments).

  18. brianschubert commented on Jun 23, 2025

    @brianschubert
    Collaborator

    the key type and value type are swapped

    D'oh! Yeah, I completely overlooked that :)

    outstanding follow-up work

    AFAIK (I'm not a mypy maintainer, so I can't speak for them), no work on HKTs is currently planned. These days mypy generally follows the Python typing spec. A major typing feature like HKTs would likely first need to fleshed out in a PEP / typing spec revision before a mypy implementation is considered

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions