PEP 557 では、データクラスがpython標準ライブラリに導入されます。
それらは@dataclass
デコレータを使用し、「デフォルトの可変名前付きタプル」であると想定されていますが、これが実際に何を意味し、どのように一般的なクラスと異なるのかがよくわかりません。
pythonデータクラスとはどのようなもので、いつ使用するのが最適ですか?
データクラスは、多くのロジックが含まれているだけでなく、状態の保存を対象とした通常のクラスです。ほとんどが属性で構成されるクラスを作成するたびに、データクラスを作成しました。
dataclasses
モジュールが行うことは、データクラスを作成するためにeasierにすることです。それはあなたのためにボイラープレートの多くの世話をします。
これは、データクラスをハッシュ可能にする必要がある場合に特に重要です。これには、__hash__
メソッドと__eq__
メソッドが必要です。デバッグを容易にするためにカスタム__repr__
メソッドを追加すると、非常に冗長になる可能性があります。
class InventoryItem:
'''Class for keeping track of an item in inventory.'''
name: str
unit_price: float
quantity_on_hand: int = 0
def __init__(
self,
name: str,
unit_price: float,
quantity_on_hand: int = 0
) -> None:
self.name = name
self.unit_price = unit_price
self.quantity_on_hand = quantity_on_hand
def total_cost(self) -> float:
return self.unit_price * self.quantity_on_hand
def __repr__(self) -> str:
return (
'InventoryItem('
f'name={self.name!r}, unit_price={self.unit_price!r}, '
f'quantity_on_hand={self.quantity_on_hand!r})'
def __hash__(self) -> int:
return hash((self.name, self.unit_price, self.quantity_on_hand))
def __eq__(self, other) -> bool:
if not isinstance(other, InventoryItem):
return NotImplemented
return (
(self.name, self.unit_price, self.quantity_on_hand) ==
(other.name, other.unit_price, other.quantity_on_hand))
dataclasses
を使用すると、次のように減らすことができます。
from dataclasses import dataclass
@dataclass(unsafe_hash=True)
class InventoryItem:
'''Class for keeping track of an item in inventory.'''
name: str
unit_price: float
quantity_on_hand: int = 0
def total_cost(self) -> float:
return self.unit_price * self.quantity_on_hand
同じクラスデコレータは、比較メソッド(__lt__
、__gt__
など)を生成し、不変性を処理することもできます。
namedtuple
クラスもデータクラスですが、デフォルトでは不変です(シーケンスでもあります)。 dataclasses
はこの点ではるかに柔軟性があり、 namedtuple
クラスと同じ役割を満たす のように簡単に構成できます。
PEPは attrs
プロジェクト に触発され、さらに多くのことができます(スロット、バリデーター、コンバーター、メタデータなどを含む)。
いくつかの例をご覧になりたい場合、最近 Advent of Code ソリューションのいくつかにdataclasses
を使用しました。 7日目 、 8日目 、 11日目 および 20日目 .
Pythonバージョン<3.7でdataclasses
モジュールを使用する場合は、 バックポートモジュール (3.6が必要)をインストールするか、attrs
プロジェクトを使用します。上記の通り。
質問は解決されました。ただし、この回答では、データクラスの基本的な理解に役立つ実用的な例をいくつか追加しています。
pythonデータクラスとは正確に何で、いつ使用するのが最適ですか?
namedtuple
などのドット付き属性アクセスその他 。「デフォルトの[s]を持つ可変名前付きタプル」
後者のフレーズの意味は次のとおりです。
namedtuple
または通常のクラスのように、ドットで区切られた属性アクセスがあります。一般的なクラスと比較して、定型コードの入力を節約できます。
以下にデータクラス機能の概要を示します(概要表の例を参照)。
以下は、デフォルトでデータクラスから取得する機能です。
属性+表現+比較
import dataclasses
@dataclasses.dataclass
#@dataclasses.dataclass() # alternative
class Color:
r : int = 0
g : int = 0
b : int = 0
次のデフォルトは、True
に自動的に設定されます。
@dataclasses.dataclass(init=True, repr=True, eq=True)
適切なキーワードがTrue
に設定されている場合、追加の機能を使用できます。
注文
@dataclasses.dataclass(order=True)
class Color:
r : int = 0
g : int = 0
b : int = 0
< > <= >=
と同様に、順序付けメソッドが実装され(演算子のオーバーロード:functools.total_ordering
)、同等のテストが強化されました。
ハッシュ可能、ミュータブル
@dataclasses.dataclass(unsafe_hash=True) # override base `__hash__`
class Color:
...
オブジェクトは潜在的に可変(望ましくない可能性があります)ですが、ハッシュが実装されています。
ハッシュ可能、不変
@dataclasses.dataclass(frozen=True) # `eq=True` (default) to be immutable
class Color:
...
ハッシュが実装され、オブジェクトの変更または属性への割り当てが許可されなくなりました。
全体的に、unsafe_hash=True
またはfrozen=True
の場合、オブジェクトはハッシュ可能です。
元の ハッシュロジックテーブル も参照してください。
次の機能を取得するには、特別なメソッドを手動で実装する必要があります。
アンパック可能
@dataclasses.dataclass
class Color:
r : int = 0
g : int = 0
b : int = 0
def __iter__(self):
yield from dataclasses.astuple(self)
最適化
@dataclasses.dataclass
class SlottedColor:
__slots__ = ["r", "b", "g"]
r : int
g : int
b : int
オブジェクトサイズが縮小されました。
>>> imp sys
>>> sys.getsizeof(Color)
1056
>>> sys.getsizeof(SlottedColor)
888
状況によっては、__slots__
は、インスタンスの作成と属性へのアクセスの速度も改善します。また、スロットはデフォルトの割り当てを許可しません。それ以外の場合は、ValueError
が発生します。
スロットの詳細については、この ブログ投稿 を参照してください。
+----------------------+----------------------+----------------------------------------------------+-----------------------------------------+
| Feature | Keyword | Example | Implement in a Class |
+----------------------+----------------------+----------------------------------------------------+-----------------------------------------+
| Attributes | init | Color().r -> 0 | __init__ |
| Representation | repr | Color() -> Color(r=0, g=0, b=0) | __repr__ |
| Comparision* | eq | Color() == Color(0, 0, 0) -> True | __eq__ |
| | | | |
| Order | order | sorted([Color(0, 50, 0), Color()]) -> ... | __lt__, __le__, __gt__, __ge__ |
| Hashable | unsafe_hash/frozen | {Color(), {Color()}} -> {Color(r=0, g=0, b=0)} | __hash__ |
| Immutable | frozen + eq | Color().r = 10 -> TypeError | __setattr__, __delattr__ |
| | | | |
| Unpackable+ | - | r, g, b = Color() | __iter__ |
| Optimization+ | - | sys.getsizeof(SlottedColor) -> 888 | __slots__ |
+----------------------+----------------------+----------------------------------------------------+-----------------------------------------+
+これらのメソッドは自動的に生成されず、データクラスに手動で実装する必要があります。
* __ne__
は 実装されていません 。
初期化後
@dataclasses.dataclass
class RGBA:
r : int = 0
g : int = 0
b : int = 0
a : float = 1.0
def __post_init__(self):
self.a : int = int(self.a * 255)
RGBA(127, 0, 255, 0.5)
# RGBA(r=127, g=0, b=255, a=127)
継承
@dataclasses.dataclass
class RGBA(Color):
a : int = 0
変換
データクラスをタプルまたは辞書に変換、 再帰的に :
>>> dataclasses.astuple(Color(128, 0, 255))
(128, 0, 255)
>>> dataclasses.asdict(Color(128, 0, 255))
{r: 128, g: 0, b: 255}
制限事項
ところでRaymond Hettinger(Pythonコア開発者)は、PyCon 2018で素晴らしい講演を行いました。
https://www.youtube.com/watch?v=T-TwcmT6Rcw&t=139
スライドはこちら: https://Twitter.com/raymondh/status/995693882812915712
PEP仕様 から:
PEP 526「変数アノテーションの構文」で定義されているタイプアノテーションを持つ変数のクラス定義を検査するクラスデコレーターが提供されます。このドキュメントでは、そのような変数はフィールドと呼ばれます。これらのフィールドを使用して、デコレーターは生成されたメソッド定義をクラスに追加して、インスタンスの初期化、repr、比較メソッド、およびオプションで仕様セクションで説明されている他のメソッドをサポートします。このようなクラスはデータクラスと呼ばれますが、実際にはクラスには特別なものはありません。デコレータは生成されたメソッドをクラスに追加し、与えられた同じクラスを返します。
@dataclass
ジェネレーターは、そうでなければ__repr__
、__init__
、__lt__
、および__gt__
のように自分で定義するクラスにメソッドを追加します。