python / python/cpython

Improve documentation on `dataclass` inheritance

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

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

docs topic-dataclasses
主要言語
Python
スター
77.2k
フォーク
36k
PR マージ指標
PR 指標を取得中

説明

Documentation

I feel like the docs on inheritance for dataclasses could be more explicit about certain behaviors relating to dataclass inheritance.

Inheritance without the dataclass decorator

Consider

@dataclass
class A:
    foo: str

@dataclass
class B:
    bar: int

@dataclass
class C(A, B):
    pass

class D(A, B):
    pass

The docs discuss inheritance for the case

When the dataclass is being created by the @dataclass decorator

but I don't think it's necessarily obvious what happens when you inherit a dataclass without decorating the subclass, i.e. class C will inherit fields from both A and B (because it's decorated as a dataclass), but class D will only inherit fields from A.

>>> C.__init__
<function __main__.C.__init__(self, bar: int, foo: str) -> None>
>>> D.__init__
<function __main__.A.__init__(self, foo: str) -> None>

Yes, it makes sense that because D doesn't specify an __init__ method it just inherits its __init__ from the first base class that has one, but I don't think it's unreasonable to have wondered if the "dataclass-like behavior" of inheriting all fields in the MRO is part of the behaviour passed on through inheriting a dataclass. You obviously expect a subclass to inherit all methods and parameters of all its parent classes, so that not applying to dataclass fields unless you use a decorator is perhaps unexpected. Especially as is_dataclass(D) == True. Perhaps the docs could explicitly specify that if you want to inherit fields from multiple dataclass then you must decorate the subclass?

Inheritance of fields from dataclasses deeper in the MRO that the immediate (non dataclass) base class doesn't have

Consider:

@dataclass
class E(D):
    pass

Given the explanation of how the MRO is searched for dataclass fields it makes sense that E does inherit fields from both A and B, even though D has only inherited from A:

>>> E.__init__
<function __main__.E.__init__(self, bar: int, foo: str) -> None>

but it feels unintuitive that by inheriting from D you get things that D itself doesn't have. I almost wonder if this counts as a design flaw - but failing that, I suggest the docs make it explicit that you can't tell just from the __dataclass_fields__ dict of a class that you're inherting what the dataclass fields of your subclass will be (in this case D.__dataclass_fields__ contains only foo), but that you need to search the entire MRO.

The dataclass decorator's arguments effects on inherited fields

Consider the following

@dataclass(kw_only=True)
class F(A):
    baz: float

The docs specify:

kw_only: If true (the default value is False), then all fields will be marked as keyword-only

so I think you'd be forgiven for wondering if all fields of F, including fields inhertied from A, would be made keyword-only. It seems only those defined in the body of F itself are made keyword-only:

>>> F.__init__
<function __main__.F.__init__(self, foo: str, *, baz: float) -> None>

I'd like to suggest making it explicit how the arguments of the dataclass decorator affect (or not) inherited fields.

Incompatability of inheriting where default arguments are defined in more than one class

Consider:

@dataclass
class G:
    foo: int
    bar: int = 1

@dataclass
class H(G):
    spam: str
    egg: str = 'chips'

The above will throw the following exception:

TypeError: non-default argument 'spam' follows default argument 'bar'

This is mentioned briefly in the documentation for the @dataclass decorator:

TypeError will be raised if a field without a default value follows a field with a default value. This is true whether this occurs in a single class, or as a result of class inheritance.

However I wonder if it might be worth repeating in the section on Inheritance, perhaps with an example, and maybe the suggestion of avoiding the issue by using keyword-only arguments.

Linked PRs
  • gh-153672

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

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

はじめの一歩

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

調査の方向性

変更を加える前に、Python ドキュメントのリンク先にある dataclasses の継承に関するセクションから始め、PR gh-153672 を確認してください。この issue の 4 つの例を既存のテキストと照合してください。完了とは、ドキュメントがデコレートされていないサブクラス、MRO に基づくフィールドの収集、継承されたフィールドに対するデコレータ引数、およびデフォルトあり/デフォルトなしの順序を明示的に扱っていることを意味します。

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

評価

技術スタック
python
領域
documentation
issue の種類
ドキュメント
難易度
3/5
見積もり時間
1〜2日
活発さ
停滞
明瞭さ
明確に書かれている
初心者へのやさしさ
42/100

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

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