1. ホーム
  2. python

[解決済み] データクラスとtyping.NamedTupleの主な使用例

2022-08-01 17:51:50

疑問点

長い話

PEP-557 は Python 標準ライブラリにデータクラスを導入し、基本的には collections.namedtupletyping.NamedTuple . そして今、私はnamedtupleがまだ良い解決策であるユースケースをどのように分けるかについて考えています。

NamedTupleに対するデータクラスの優位性

もちろん、すべての功績は dataclass が必要であれば

  • ミュータブルオブジェクト
  • 継承のサポート
  • property デコレータ、管理可能な属性
  • 箱から出してすぐに使えるメソッド定義、またはカスタマイズ可能なメソッド定義

データクラスの利点は、同じ PEP で簡単に説明されています。 なぜnamedtupleを使用しないのか .

Q: どのような場合にnamedtupleの方が良いのでしょうか?

しかし、namedtupleに対する反対の質問はどうでしょうか:なぜdataclassを使用しないのでしょうか? おそらくパフォーマンスの観点からnamedtupleの方が優れているのでしょうが、それについての確認はまだありません。

次のような状況を考えてみましょう。

静的に定義されたフィールド、タイプヒンティング、名前付きアクセスを持つ小さなコンテナにページの次元を保存するつもりです。さらなるハッシュ化、比較などは必要ありません。

NamedTupleのアプローチ。

from typing import NamedTuple

PageDimensions = NamedTuple("PageDimensions", [('width', int), ('height', int)])

DataClassのアプローチ。

from dataclasses import dataclass

@dataclass
class PageDimensions:
    width: int
    height: int

どの解決策が望ましいか、またその理由は?

追伸:この質問は、重複していませんが あれ というのは、ここでは ケース についてではなく、namedtupleの方が良い場合について尋ねているのです。 その違い (聞く前にドキュメントとソースをチェックしました)

どのように解決するのですか?

それはあなたのニーズによって異なります。それぞれに利点があります。

PyCon 2018でのDataclassesの解説はこちらです。 Raymond Hettinger - Dataclasses: すべてのコードジェネレータを終わらせるコードジェネレータ

この記事では Dataclass の実装はすべてPythonで書かれています。 であるのに対し NamedTuple では、これらの動作はすべて無償で提供されます。 NamedTuple から継承しているからです。 tuple . また tuple 構造はCで書かれているので 標準的なメソッドは NamedTuple (ハッシュ、比較など)の方が速いです。

また、以下のことに注意してください。 Dataclassdict 一方 NamedTupletuple . このように、これらの構造を使うことにはメリットとデメリットがあるのです。例えば、スペース使用量は NamedTuple の方がスペースが少なくて済みますが、時間アクセスは Dataclass .

どうぞ、私の実験をご覧ください。

In [33]: a = PageDimensionsDC(width=10, height=10)

In [34]: sys.getsizeof(a) + sys.getsizeof(vars(a))
Out[34]: 168

In [35]: %timeit a.width
43.2 ns ± 1.05 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)

In [36]: a = PageDimensionsNT(width=10, height=10)

In [37]: sys.getsizeof(a)
Out[37]: 64

In [38]: %timeit a.width
63.6 ns ± 1.33 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)

の属性数を増やすと NamedTuple のアクセス時間は同じように短いままです。なぜなら、それぞれの属性に対して、属性の名前を持つプロパティが作成されるからです。例えば、私たちのケースでは、新しいクラスの名前空間の部分は次のようになります。

from operator import itemgetter

class_namespace = {
...
    'width': property(itemgetter(0, doc="Alias for field number 0")),
    'height': property(itemgetter(0, doc="Alias for field number 1"))**
}

どのような場合にnamedtupleがまだ良い選択なのでしょうか?

データ構造が必要なとき/できるとき immutable, hashable, iterable, unpackable, comparable であることが必要な場合、次のようにします。 NamedTuple . もし、何か より複雑なもの、例えば継承の可能性などが必要な場合 を使用します。 Dataclass .