関連するデータを一緒にクラスター化することが理にかなっている場合があります。私は辞書でそうする傾向があります、例えば、
self.group = dict(a=1, b=2, c=3)
print self.group['a']
同僚の1人がクラスを作成することを好む
class groupClass(object):
def __init__(a, b, c):
self.a = a
self.b = b
self.c = c
self.group = groupClass(1, 2, 3)
print self.group.a
クラスメソッドを定義していないことに注意してください。
コードの行数を最小限に抑えたいため、dictを使用しています。私の同僚は、クラスを使用するとコードが読みやすくなり、将来クラスにメソッドを追加しやすくなると考えています。
どちらが好きですか?
クラスメソッドをまったく定義していない場合は、dictまたは namedtuple がはるかに理にかなっていると思います。シンプル+ビルトインが良いです!しかし、それぞれに。
背景
SF Pythonの2017年ホリデーミートアップで、R。Hettingerが属性ベースの代替データコンテナの概要を発表しました。彼の Tweet と彼の slide deck を参照してください。また、彼はPyCon 2018でデータクラスについて talk を行いました。
他のデータコンテナタイプについては、この 記事 および主にPython 3ドキュメント(下記のリンクを参照)で説明されています。
以下は、標準ライブラリへのrecordclass
の追加に関する python-ideas メーリングリストに関する議論です。
オプション
標準ライブラリの代替
collections.namedtuple
:属性を持つタプル(seminal recipe を参照)typing.NamedTuple
:サブクラス化可能なタプル(namedtuple
と比較するこの post を参照)types.SimpleNamespace
:オプションのクラス宣言を含む単純なクラスtypes.MappingProxy
:読み取り専用辞書enum.Enum
:関連する定数の制約付きコレクション(クラスのように動作します)dataclasses.dataclass
:default/boilerplate-lessクラスを持つ可変のnamedtuple外部オプション
SimpleNamedspace
のインスピレーション)どれですか?
使用するオプションの決定は、状況によって異なります(以下の例を参照)。通常、昔ながらの可変辞書または不変の名前付きタプルで十分です。データクラスは、 attrs プロジェクトに触発されたボイラープレートの削減を約束して、可変性と オプションの不変性 の両方を提供する最新の追加(Python 3.7a)です。
例
import typing as typ
import collections as ct
import dataclasses as dc
# Problem: You want a simple container to hold personal data.
# Solution: Try a NamedTuple.
>>> class Person(typ.NamedTuple):
... name: str
... age: int
>>> a = Person("bob", 30)
>>> a
Person(name='bob', age=30)
# Problem: You need to change age each year, but namedtuples are immutable.
# Solution: Use assignable attributes of a traditional class.
>>> class Person:
... def __init__(self, name, age):
... self.name = name
... self.age = age
>>> b = Person("bob", 30)
>>> b.age = 31
>>> b
<__main__.Person at 0x4e27128>
# Problem: You lost the pretty repr and want to add comparison features.
# Solution: Use included repr and eq features from the new dataclasses.
>>> @dc.dataclass(eq=True)
... class Person:
... name: str
... age: int
>>> c = Person("bob", 30)
>>> c.age = 31
>>> c
Person(name='bob', age=31)
>>> d = Person("dan", 31)
>>> c != d
True
[〜#〜] yagni [〜#〜] に従い、辞書を使用することを好みます。
data classes と呼ばれる、探しているものを正確に実装することを目的とした新しい提案があります。それを見てください。
Dictよりもクラスを使用するのは好みの問題です。個人的には、キーがアプリオリに知られていないときに辞書を使うことを好みます。 (マッピングコンテナとして)。
クラスを使用してデータを保持することは、クラス属性にドキュメントを提供できることを意味します。
個人的には、おそらくクラスを使用する最大の理由は、IDEのオートコンプリート機能を利用することです。 (技術的には不十分な理由ですが、実践には非常に役立ちます)
ところで、Python 3.7実装@ dataclass は、データコンテナとしてクラスを実装する最も簡単で効率的な方法です。
@dataclass
class Data:
a: list
b: str #default variables go after non default variables
c: bool = False
def func():
return A(a="hello")
print(func())
出力は:hello
になります
ケースクラスのようなScalaに似すぎており、クラスをコンテナとして使用する最も簡単な方法です。
あなたの方法は優れています。あなたが成功する可能性が低いので、あまりにも未来を予測しようとしないでください。
ただし、 C構造体のようなものを使用することが理にかなっている場合があります 。たとえば、すべてにdictsを使用するのではなく、異なるタイプを識別したい場合です。
Dictから継承したラッパークラスを使用して、dictとクラスの利点を組み合わせることができます。定型コードを記述する必要はなく、同時にドット表記を使用できます。
class ObjDict(dict):
def __getattr__(self,attr):
return self[attr]
def __setattr__(self,attr,value):
self[attr]=value
self.group = ObjDict(a=1, b=2, c=3)
print self.group.a
メソッドのないクラスを使用するとコードが読みやすくなることに同意しません。通常、データだけでなく、クラスから機能を期待します。
だから、機能の必要性が生じるまで辞書を探して、クラスのコンストラクタは辞書を受け取ることができます:-)
Prodict :について
group = Prodict(a=1, b=2, c=3)
group.d = 4
そして、自動タイプ変換と自動コード完了(インテリセンス)が必要な場合:
class Person(Prodict):
name: str
email: str
rate: int
john = Person(name='John', email='[email protected]')
john.rate = 7
john.age = 35 # dynamic
それをサポートする言語では、struct
を使用します。辞書は、少なくとも私の知る限り、Pythonの構造に最も近いものです。
言うまでもなく、reallyしたい場合は、とにかくメソッドを辞書に追加できます;)
口述は明らかにそのような状況に適しています。それはそのユースケースのために特別に設計されました。実際にクラスをクラスとして使用する場合を除き、車輪を再発明し、追加のオーバーヘッドを発生させたり、悪い辞書として機能するクラスのスペースを無駄にしたりすることはありません(辞書機能はありません)。
メモリフットプリントを気にしない場合は、 dict 、 namedtuple 、 dataclass 、または___slots__
_のクラスのみが適切な選択肢です。
しかし、いくつかの属性を持つ何百万ものオブジェクトを作成する必要がある場合、 recordclass libraryに基づくソリューションがあります。
_from recordclass import make_dataclass
C = make_dataclass("C", ('a', 'b', 'c'))
c = C(1, 2, 3)
_
クラス定義と同じ:
_from recordclass import dataobject
class C(dataobject):
a:int
b:int
c:int
c = C(1, 2, 3)
_
最小メモリフットプリント= sizeof(PyObject_HEAD) + 3*sizeof(PyObject*)
バイトです。
比較のために___slots__
_ベースのバリアントにはsizeof(PyGC_Head) + sizeof(PyObject_HEAD) + 3*sizeof(PyObject*)
バイトが必要です。