web-dev-qa-db-ja.com

クラスまたは辞書を使用する必要がありますか?

次のように、フィールドのみを含みメソッドを含まないクラスがあります:

class Request(object):

    def __init__(self, environ):
        self.environ = environ
        self.request_method = environ.get('REQUEST_METHOD', None)
        self.url_scheme = environ.get('wsgi.url_scheme', None)
        self.request_uri = wsgiref.util.request_uri(environ)
        self.path = environ.get('PATH_INFO', None)
        # ...

これは簡単に辞書に翻訳できます。このクラスは将来の追加に対してより柔軟であり、__slots__。それでは、代わりにdictを使用する利点がありますか? dictはクラスよりも高速ですか?そして、スロットを持つクラスよりも高速ですか?

82
deamon

なぜこれを辞書にするのですか?利点は何ですか?後でコードを追加したい場合はどうなりますか?あなたの__init__コードは行く?

クラスは、関連データ(および通常はコード)をバンドルするためのものです。

辞書は、キーと値の関係を格納するためのものです。通常、キーはすべて同じタイプであり、すべての値も1つのタイプです。キー/属性名がすべて事前にわかっていない場合にデータをバンドルするのに役立つことがありますが、多くの場合、これは設計に問題があることを示しています。

これをクラスにしてください。

23
adw

クラスの追加メカニズムが必要でない限り、辞書を使用します。 namedtuple をハイブリッドアプローチに使用することもできます。

>>> from collections import namedtuple
>>> request = namedtuple("Request", "environ request_method url_scheme")
>>> request
<class '__main__.Request'>
>>> request.environ = "foo"
>>> request.environ
'foo'

ここでのパフォーマンスの違いは最小限に抑えられますが、辞書が高速でなければ驚いたでしょう。

38
Katriel

python isの下にあるクラス。この場合、次の理由でクラスの恩恵を受けると思います。

  • すべてのロジックは単一の関数内にあります
  • 更新が簡単で、カプセル化されたままです
  • 後で何かを変更する場合は、簡単に同じインターフェイスを維持できます
35
Bruce Armstrong

それぞれの使い方は、私にとってはあまりにも主観的すぎると思うので、数字だけに固執します。

Dict内の変数、new_styleクラス、およびスロットを持つnew_styleクラスの作成と変更にかかる時間を比較しました。

ここに私がそれをテストするために使用したコードがあります(それは少し面倒ですが、それは仕事をします)

import timeit

class Foo(object):

    def __init__(self):

        self.foo1 = 'test'
        self.foo2 = 'test'
        self.foo3 = 'test'

def create_dict():

    foo_dict = {}
    foo_dict['foo1'] = 'test'
    foo_dict['foo2'] = 'test'
    foo_dict['foo3'] = 'test'

    return foo_dict

class Bar(object):
    __slots__ = ['foo1', 'foo2', 'foo3']

    def __init__(self):

        self.foo1 = 'test'
        self.foo2 = 'test'
        self.foo3 = 'test'

tmit = timeit.timeit

print 'Creating...\n'
print 'Dict: ' + str(tmit('create_dict()', 'from __main__ import create_dict'))
print 'Class: ' + str(tmit('Foo()', 'from __main__ import Foo'))
print 'Class with slots: ' + str(tmit('Bar()', 'from __main__ import Bar'))

print '\nChanging a variable...\n'

print 'Dict: ' + str((tmit('create_dict()[\'foo3\'] = "Changed"', 'from __main__ import create_dict') - tmit('create_dict()', 'from __main__ import create_dict')))
print 'Class: ' + str((tmit('Foo().foo3 = "Changed"', 'from __main__ import Foo') - tmit('Foo()', 'from __main__ import Foo')))
print 'Class with slots: ' + str((tmit('Bar().foo3 = "Changed"', 'from __main__ import Bar') - tmit('Bar()', 'from __main__ import Bar')))

そして、これが出力です...

作成...

Dict: 0.817466186345
Class: 1.60829183597
Class_with_slots: 1.28776730003

変数を変更しています...

Dict: 0.0735140918748
Class: 0.111714198313
Class_with_slots: 0.10618612142

したがって、変数を保存するだけで速度が必要で、多くの計算を行う必要がない場合は、dictを使用することをお勧めします(常にメソッドのように見える関数を作成できます)。ただし、本当にクラスが必要な場合は、覚えておいてください-常に__slots__を使用してください。

注意:

both new_styleおよびold_styleクラスで 'Class'をテストしました。 old_styleクラスは、作成は高速ですが、変更は遅くなります(タイトループで多数のクラスを作成している場合(ヒント:間違っています))。

また、私のものは古くて遅いため、変数の作成と変更の時間はコンピューターによって異なる場合があります。 「実際の」結果を確認するには、必ず自分でテストしてください。

編集:

後でnamedtupleをテストしました。変更することはできませんが、10000個のサンプル(またはそのようなもの)を作成するには1.4秒かかったため、辞書は確かに最速です。

I dict関数を変更するキーと値を含め、dictを作成するときにdictを含む変数の代わりにdictを返すと、.8秒ではなく0.65になります=

class Foo(dict):
    pass

作成はスロットを持つクラスに似ており、変数の変更が最も遅い(0.17秒)ため、これらのクラスを使用しないでくださいです。辞書(速度)またはオブジェクトから派生したクラス(「構文キャンディ」)を求めて

19
alexpinho98

クラスは、リクエストに関連するあらゆる種類の情報なので、お勧めします。辞書を使用する人であれば、保存されるデータは本質的にはるかに似ていると思います。私が自分自身に従う傾向があるガイドラインは、キーと値のペアのセット全体をループして何かをしたい場合、辞書を使用することです。そうでなければ、データは明らかに基本的なキー->値のマッピングよりもはるかに多くの構造を持っているため、クラスがより良い代替手段になる可能性が高いことを意味します。

したがって、クラスに固執します。

5
Stigma

ケーキを持って食べてもいいかもしれません。つまり、クラスと辞書インスタンスの両方の機能を提供するものを作成できます。 ActiveStateの Dɪᴄᴛɪᴏɴᴀʀʏᴡɪᴛʜᴛʏʟᴇ-sᴛʏʟᴇᴀᴄᴄᴇss レシピとその方法に関するコメントを参照してください。

サブクラスではなく通常のクラスを使用することにした場合、 Tʜᴇsɪᴍᴘʟᴇʙᴜᴛʜᴀɴᴅʏᴄᴏʟʟᴇᴄᴛᴏʀ "ᴄᴏʟʟᴇᴄᴛᴏʀᴏғᴀʙᴜɴᴄʜᴛᴜғғɴᴀᴍᴇᴅsᴛᴜғғ"ᴄʟᴀss レシピ(Alex Martelli) very柔軟性があり、あなたがやっているように見える種類のことに対して有用です(つまり、情報の比較的単純なアグリゲーターを作成します)。クラスなので、メソッドを追加することで簡単に機能を拡張できます。

最後に、クラスメンバーの名前は有効なPython識別子である必要がありますが、ディクショナリキーはそうではないことに注意してください。文字列ではないもの)。

更新

クラス object__dict__を持たない)サブクラス SimpleNamespace (これは1つあります)がPython 3.3に追加されましたが、もう1つの代替手段です。

3
martineau

obj.bla = 5の代わりにobj['bla'] = 5のような構文キャンディーだけを実現したい場合、特にそれを何度も繰り返さなければならない場合は、Martineausの提案のように単純なコンテナークラスを使用することができます。それにもかかわらず、そこのコードは非常に肥大化しており、不必要に遅いです。そのようにシンプルに保つことができます:

class AttrDict(dict):
    """ Syntax candy """
    __getattr__ = dict.__getitem__
    __setattr__ = dict.__setitem__
    __delattr__ = dict.__delitem__

namedtuplesまたは__slots__を持つクラスに切り替えるもう1つの理由は、メモリ使用量です。辞書はリスト型よりもかなり多くのメモリを必要とするので、これは考慮すべき点です。

とにかく、あなたの特定のケースでは、現在の実装から切り替わる動機はないようです。これらの何百万ものオブジェクトを保持していないようですので、リスト派生型は必要ありません。また、実際には__init__内にいくつかの機能ロジックが含まれているため、AttrDictを使用しないでください。

3
Michael