web-dev-qa-db-ja.com

Pythonクラスをデータコンテナとして使用する

関連するデータを一緒にクラスター化することが理にかなっている場合があります。私は辞書でそうする傾向があります、例えば、

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を使用しています。私の同僚は、クラスを使用するとコードが読みやすくなり、将来クラスにメソッドを追加しやすくなると考えています。

どちらが好きですか?

47
gaefan

クラスメソッドをまったく定義していない場合は、dictまたは namedtuple がはるかに理にかなっていると思います。シンプル+ビルトインが良いです!しかし、それぞれに。

43
Joe Kington

背景

SF Pythonの2017年ホリデーミートアップで、R。Hettingerが属性ベースの代替データコンテナの概要を発表しました。彼の Tweet と彼の slide deck を参照してください。また、彼はPyCon 2018でデータクラスについて talk を行いました。

他のデータコンテナタイプについては、この 記事 および主にPython 3ドキュメント(下記のリンクを参照)で説明されています。

以下は、標準ライブラリへのrecordclassの追加に関する python-ideas メーリングリストに関する議論です。

オプション

標準ライブラリの代替

外部オプション

  • records :変更可能な名前付きタプル( recordclass も参照)
  • bunches :辞書への属性アクセスを追加(SimpleNamedspaceのインスピレーション)
  • boxドットスタイルのルックアップ 機能で辞書をラップ
  • attrdict :キーまたは属性としてマッピングの要素にアクセスします
  • fields :コンテナクラスからボイラープレートを削除します。
  • namedlist :E.スミスによるデフォルトの可変、タプルのようなコンテナ

どれですか?

使用するオプションの決定は、状況によって異なります(以下の例を参照)。通常、昔ながらの可変辞書または不変の名前付きタプルで十分です。データクラスは、 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
26
pylang

[〜#〜] yagni [〜#〜] に従い、辞書を使用することを好みます。

data classes と呼ばれる、探しているものを正確に実装することを目的とした新しい提案があります。それを見てください。

Dictよりもクラスを使用するのは好みの問題です。個人的には、キーがアプリオリに知られていないときに辞書を使うことを好みます。 (マッピングコンテナとして)。

クラスを使用してデータを保持することは、クラス属性にドキュメントを提供できることを意味します。

個人的には、おそらくクラスを使用する最大の理由は、IDEのオートコンプリート機能を利用することです。 (技術的には不十分な理由ですが、実践には非常に役立ちます)

7
nesdis

ところで、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に似すぎており、クラスをコンテナとして使用する最も簡単な方法です。

5
james.bondu

あなたの方法は優れています。あなたが成功する可能性が低いので、あまりにも未来を予測しようとしないでください。

ただし、 C構造体のようなものを使用することが理にかなっている場合があります 。たとえば、すべてにdictsを使用するのではなく、異なるタイプを識別したい場合です。

5

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
3
agdk26

メソッドのないクラスを使用するとコードが読みやすくなることに同意しません。通常、データだけでなく、クラスから機能を期待します。

だから、機能の必要性が生じるまで辞書を探して、クラスのコンストラクタは辞書を受け取ることができます:-)

2
Vinko Vrsalovic

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
1
Ramazan Polat

それをサポートする言語では、structを使用します。辞書は、少なくとも私の知る限り、Pythonの構造に最も近いものです。

言うまでもなく、reallyしたい場合は、とにかくメソッドを辞書に追加できます;)

1
Brian S

口述は明らかにそのような状況に適しています。それはそのユースケースのために特別に設計されました。実際にクラスをクラスとして使用する場合を除き、車輪を再発明し、追加のオーバーヘッドを発生させたり、悪い辞書として機能するクラスのスペースを無駄にしたりすることはありません(辞書機能はありません)。

1
jeffcook2150

メモリフットプリントを気にしない場合は、 dictnamedtupledataclass 、または___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*)バイトが必要です。

0
intellimath