web-dev-qa-db-ja.com

非特定のデータクラスのインスタンスのタイプヒント

任意のdataclassのインスタンスを受け入れる関数があります。そのための適切なタイプのヒントは何でしょうか?

pythonドキュメントで公式なものが見つかりませんでした


これは私がやってきたことですが、私はそれが正しいとは思いません

from typing import Any, NewType

DataClass = NewType('DataClass', Any)
def foo(obj: DataClass):
    ...

別のアイデアは、これらのクラス属性__dataclass_fields____dataclass_params__Protocol を使用することです。

9
moshevi

その名前にもかかわらず、dataclasses.dataclassはクラスインターフェイスを公開しません。これにより、カスタムクラスを便利な方法で宣言できるため、データコンテナとして使用されることが明確になります。したがって、理論的には、データクラスは実際には単なる通常のクラスであるため、データクラスでのみ機能するものを作成する機会はほとんどありません。

実際には、とにかくデータクラスのみの関数を宣言したい理由はいくつかあります。それを実現するには2つの方法があります。


静的型チェッカーを使用してプロトコルを作成するright方法

from dataclasses import dataclass
from typing import Dict

from typing_extensions import Protocol

class IsDataclass(Protocol):
    # as already noted in comments, checking for this attribute is currently
    # the most reliable way to ascertain that something is a dataclass
    __dataclass_fields__: Dict

def dataclass_only(x: IsDataclass):
    ...  # do something that only makes sense with a dataclass

@dataclass
class A:
    pass

dataclass_only(A())  # a static type check should show that this line is fine

このアプローチは、あなたが質問でほのめかしたものでもありますが、3つの欠点があります。

  • 静的型チェックを行うには、 mypy などのサードパーティライブラリが必要です。
  • インストールする必要があります typing_extensions 同様に、Protocolはまだコアtypingモジュールの一部ではないため
  • 最後になりましたが、このようにデータクラスのプロトコルを使用する 現在は機能しません

もう少し [〜#〜] eafp [〜#〜] -実際に機能するインスピレーション

from dataclasses import is_dataclass

def dataclass_only(x):
    """Do something that only makes sense with a dataclass.

    Raises:
        ValueError if something that is not a dataclass is passed.

    ... more documentation ...
    """
    if not is_dataclass(x):
        raise ValueError(f"'{x.__class__.__name__}' is not a dataclass!")
    ...

このアプローチでは、ドキュメントのおかげで、このコードのメンテナーまたはユーザーにとって動作は依然として非常に明確です。ただし、欠点は、コードの静的分析(IDEによるタイプヒントを含む)が得られないことです。

9
Arne