python / python/typing

Allow Self or Self-like type arguments for generics

オープン
#2,276 コメント 2 件 リアクション 0 件 担当者 0 名 GitHub で見る

まだ誰も着手していません。

topic: feature
主要言語
Python
スター
1.8k
フォーク
302
平均マージ
23時間
マージ済み PR(30日)
8

説明

from contextlib import AbstractContextManager

class ManagerA(AbstractContextManager):
    def __init__(self, x: int) -> None:
        self._x = x
    def __enter__(self) -> int:
        return self._x

This can raise type check errors because AbstractContextManager is a generic type, and this code omits the type argument. Fair enough, and easy to fix:

from contextlib import AbstractContextManager

class ManagerA(AbstractContextManager[int]):
    def __init__(self, x: int) -> None:
        self._x = x
    def __enter__(self) -> int:
        return self._x

But what if we are not overriding the __enter__ method? Consider this example:

from contextlib import AbstractContextManager

class ManagerB(AbstractContextManager):
    def __init__(self, x: int) -> None:
        self._x = x
    # Other methods are defined, but __enter__ is not overridden

In this case, there is no great way to accurate type annotate it in order to reflect the fact that the __enter__ method always returns the object that it was called on (which is the default implementation of the __enter__ method in AbstractContextManager.

If we were willing to mark this class as final, then we could annotate it as:

class ManagerB(AbstractContextManager["ManagerB"]):

And that would work. But if we want to be able to subclass it, then this would no longer be accurate. Consider:

from contextlib import AbstractContextManager
from types import TracebackType
from typing import Type

class ManagerB(AbstractContextManager["ManagerB"]):
    def __init__(self, x: int) -> None:
        self._x = x

    # Other methods are defined, but __enter__ is not overridden

    def __exit__(
            self, exc_type: Type[BaseException] | None,
            exc_val: BaseException | None,
            exc_tb: TracebackType | None) -> bool | None:
        return None

class SubManager(ManagerB):
    def harf(self) -> None:
        print("harf")

with SubManager(1) as subman:
    subman.harf()

This runs fine, but the type checker will rightly complain. The annotations indicate that subman should be of type ManagerB, but ManagerB has no harf attribute.

It feels like it would be appropriate to use:

class ManagerB(AbstractContextManager[Self]):

But Self is not allowed to be used in this context.

The feature I would like to see is some way to accurately handle non-final subclasses of AbstractContextManager that do not overwrite the __enter__ method (or that overwrite it but still return self).

Obviously this issue applies generally to generics -- this is just an obvious example since the Python implementation of that class makes it impossible to type annotate many of its subclasses. But ideally any solution would apply generally to generics -- essentially giving a way to use Self (or something Self-like) as the type argument for a generic.

コントリビューションガイド

このリポジトリのコントリビューションガイドは索引されていません

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

調査の方向性

AbstractContextManager の例と、Self をジェネリック型引数として使用することに関して示されている制限から始めます。ManagerB と SubManager に対して望まれる動作を、Self、サブクラス化、ジェネリック型引数に関する現在のルールと比較します。一般的な解決策によって具体的なサブクラスが正確に推論され、オーバーライドされた enter メソッドと継承された enter メソッドの正しい動作も維持されれば完了です。

索引モデルが issue の本文から書いたものです。

評価

技術スタック
python
領域
tooling
issue の種類
機能追加
難易度
5/5
見積もり時間
1週間以上
活発さ
静か
明瞭さ
おおむね明確
初心者へのやさしさ
35/100

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。