2013-10-28 15 views
23

Pythonのライブラリでは、dict型以上のサブクラスdictをPythonの2つの実装で使用できるようになりました。OrderedDict対defaultdict対dict

可能であれば、dict.setdefaultを使用すると、Pythonの主張者は常にdefaultdictに優先しています。とき、これまで可能な代替の使用のための辞書としてThis technique is simpler and faster than an equivalent technique using dict.setdefault():同様の方法で

は、アイテムをソート続いdictを使用して上OrderedDictを使用して、順序を維持しないということであってもdoc引用符が好ましいです。

上記の両方のケースでは、コードは間違いなくクリーンですが、パフォーマンス上のペナルティを犠牲にしています。

答えると、質問python unique list based on itemの1についてコメントしながらdefaultdictOrderedDictを使用しているとき、私はネイティブdict上でパフォーマンスの低下につまずきました。それはまた、データのサイズはパフォーマンスの優位性に重要ではないようだdictソリューションは他のものを超えています。

私はThere should be one-- and preferably only one --obvious way to do it.を信じていますので、どのような方法が良いですか?

+3

を 'defaultdict'は必ずしも正規よりも遅くありません'dict'。タイミングには欠陥があります。タイミング*にはオブジェクトの作成*が含まれます。それ以外に、さまざまなタイプのパフォーマンスがあり、メンテナンスが容易です。パフォーマンスを測定する基準は指定していません。 **仕事のための**正しいツールを使用してください。 –

+1

もう一度やり直してください:あなたのユースケースの*独自のタイミングを実行してください。あなたが挙げるタイミングは、小さなデータセットのためのものであり、辞書オブジェクトの作成を含みます。なぜなら、小さなデータセットで不公平に結果を歪める 'defaultdict(...)'ファクトリコール(グローバルルックアップ、スタックプッシュ、呼び出し)よりも、dictリテラル( '{}')が* far * 'defaultdict'の方が遅いという前提には欠陥があります。 –

+2

私は 'defaultdict'が' dict.setdefault'を呼び出さなければならないことにしばしば同意しますが、なぜ 'OrderedDict'が' dict'に対して「可能な限り優先」されるのですか?私は、キーが辞書に挿入された順序について気にしていなかったと思います。これは単に新しいキーのデフォルト値を提供するよりもはるかに特有の機能です。 – chepner

答えて

6

私はあなたの前提がの1つの好ましい方法であると感じています - は保持しません。

メンテナンスを集中的コード(例えば進化するユーティリティクラスのオプションパーサ)私はいつも、クリーンなコードのために行くだろうので、他の人と:私はさまざまな要件と少なくとも2例を参照してください私はより簡単に新しい機能を実装することができます。少量(例えば、ユーザ設定)が処理されるので、性能は重要ではない。

データ処理タスクにおけるパフォーマンスが重要なアルゴリズムの実装では、私は非常に速く実行ためのビットより冗長なコードを書く気にしないだろうが。アルゴリズムが変更される可能性が低い場合、読みにくいコードが問題になることはありません。

+0

'データ処理タスクでパフォーマンスクリティカルなアルゴリズムを実装すると、はるかに高速な実行のためにもう少し冗長なコードを書いても構いません。このようなシナリオでは、ネイティブコードで実装を記述しないのはなぜですか? 「ただ一つの好ましい方法」..私の前提ではなく、Tim Petersのガイドラインです。 – Abhijit

+0

完全に指定されたユースケース(および完全に既知の境界条件)については、* 1 *望ましい方法で解決する可能性があります。しかし、私はそれらのケースがまれであると主張するでしょう。ほとんどの場合、いくつかのトレードオフ(パフォーマンス、可読性、一般性、...)が行われなければならず、好ましい方法は基本的に、*認識された不具合*が最も少ないオプションの主観的選択です。 – ojdo

+0

ROIだけです。 PythonをCythonに書き込む、PyPyに書き込むという複雑さは、株式pythonよりも高く、Cライブラリをコンパイルすることはまだまだ複雑です。途中で行く(より速いネイティブのPythonソリューション)のトレードオフがそれに値すると判断することができますが、遠くに行くことのトレードオフ(ネイティブライブラリ)はそうではありません。 –

43

1つの答えはなく、真で唯一のものではありません。多くの変数の中では、次の要素に依存します。

  1. データセットのサイズ。
  2. ユニークキーの数。
  3. defaultdictの基礎となる工場の速度。
  4. OrderDictのスピードとその後の注文ステップ。
  5. Pythonのバージョン。

は、私はいくつかの一般論を一般化する嫌うをですが、ここでは、次のとおりです。

  1. 声明This technique is simpler and faster than an equivalent technique using dict.setdefault()はちょうどフラット間違っています。それはデータに依存します。
  2. setdefaultは、小さなデータセットではより高速で簡単です。
  3. defaultdictは、より均質なキーセットを持つより大きいデータセットの方が高速です。
  4. setdefaultは、より異機種のキーセットで利点があります。
  5. これらの結果は、Python 3とPython 2では異なります。
  6. OrderedDictは、順序や順序に依存するアルゴリズム以外のすべての場合において、再構成または並べ替えが容易ではありません。
  7. ほとんどの場合、Python 3はほとんどの操作が高速です。
  8. Python 3.6のdictが挿入命令で注文されるようになりました。

唯一の真実:それに依存します! 3つの手法はすべて役に立ちます。ここ

を示すいくつかのタイミング・コードである:

from __future__ import print_function 
from collections import defaultdict 
from collections import OrderedDict 

try: 
    t=unichr(100) 
except NameError: 
    unichr=chr 

def f1(li): 
    '''defaultdict''' 
    d = defaultdict(list) 
    for k, v in li: 
     d[k].append(v) 
    return d.items() 

def f2(li): 
    '''setdefault''' 
    d={} 
    for k, v in li: 
     d.setdefault(k, []).append(v) 
    return d.items() 

def f3(li): 
    '''OrderedDict''' 
    d=OrderedDict() 
    for k, v in li: 
     d.setdefault(k, []).append(v) 
    return d.items()  


if __name__ == '__main__': 
    import timeit 
    import sys 
    print(sys.version) 
    few=[('yellow', 1), ('blue', 2), ('yellow', 3), ('blue', 4), ('red', 1)] 
    fmt='{:>12}: {:10.2f} micro sec/call ({:,} elements, {:,} keys)' 
    for tag, m, n in [('small',5,10000), ('medium',20,1000), ('bigger',1000,100), ('large',5000,10)]: 
     for f in [f1,f2,f3]: 
      s = few*m 
      res=timeit.timeit("{}(s)".format(f.__name__), setup="from __main__ import {}, s".format(f.__name__), number=n) 
      st=fmt.format(f.__doc__, res/n*1000000, len(s), len(f(s))) 
      print(st) 
      s = [(unichr(i%0x10000),i) for i in range(1,len(s)+1)] 
      res=timeit.timeit("{}(s)".format(f.__name__), setup="from __main__ import {}, s".format(f.__name__), number=n) 
      st=fmt.format(f.__doc__, res/n*1000000, len(s), len(f(s))) 
      print(st)    
     print() 

パイソン2.7結果:

2.7.5 (default, Aug 25 2013, 00:04:04) 
[GCC 4.2.1 Compatible Apple LLVM 5.0 (clang-500.0.68)] 
defaultdict:  10.20 micro sec/call (25 elements, 3 keys) 
defaultdict:  21.08 micro sec/call (25 elements, 25 keys) 
    setdefault:  13.41 micro sec/call (25 elements, 3 keys) 
    setdefault:  18.24 micro sec/call (25 elements, 25 keys) 
OrderedDict:  49.47 micro sec/call (25 elements, 3 keys) 
OrderedDict:  102.16 micro sec/call (25 elements, 25 keys) 

defaultdict:  28.28 micro sec/call (100 elements, 3 keys) 
defaultdict:  79.78 micro sec/call (100 elements, 100 keys) 
    setdefault:  45.68 micro sec/call (100 elements, 3 keys) 
    setdefault:  68.66 micro sec/call (100 elements, 100 keys) 
OrderedDict:  117.78 micro sec/call (100 elements, 3 keys) 
OrderedDict:  343.17 micro sec/call (100 elements, 100 keys) 

defaultdict: 1123.60 micro sec/call (5,000 elements, 3 keys) 
defaultdict: 4250.44 micro sec/call (5,000 elements, 5,000 keys) 
    setdefault: 2089.86 micro sec/call (5,000 elements, 3 keys) 
    setdefault: 3803.03 micro sec/call (5,000 elements, 5,000 keys) 
OrderedDict: 4399.16 micro sec/call (5,000 elements, 3 keys) 
OrderedDict: 16279.14 micro sec/call (5,000 elements, 5,000 keys) 

defaultdict: 5609.39 micro sec/call (25,000 elements, 3 keys) 
defaultdict: 25351.60 micro sec/call (25,000 elements, 25,000 keys) 
    setdefault: 10267.00 micro sec/call (25,000 elements, 3 keys) 
    setdefault: 24091.51 micro sec/call (25,000 elements, 25,000 keys) 
OrderedDict: 22091.98 micro sec/call (25,000 elements, 3 keys) 
OrderedDict: 94028.00 micro sec/call (25,000 elements, 25,000 keys) 

パイソン3.3結果:

3.3.2 (default, May 21 2013, 11:50:47) 
[GCC 4.2.1 Compatible Apple Clang 4.1 ((tags/Apple/clang-421.11.66))] 
defaultdict:  8.58 micro sec/call (25 elements, 3 keys) 
defaultdict:  21.18 micro sec/call (25 elements, 25 keys) 
    setdefault:  10.42 micro sec/call (25 elements, 3 keys) 
    setdefault:  14.58 micro sec/call (25 elements, 25 keys) 
OrderedDict:  45.43 micro sec/call (25 elements, 3 keys) 
OrderedDict:  92.69 micro sec/call (25 elements, 25 keys) 

defaultdict:  20.47 micro sec/call (100 elements, 3 keys) 
defaultdict:  77.48 micro sec/call (100 elements, 100 keys) 
    setdefault:  34.22 micro sec/call (100 elements, 3 keys) 
    setdefault:  54.86 micro sec/call (100 elements, 100 keys) 
OrderedDict:  107.37 micro sec/call (100 elements, 3 keys) 
OrderedDict:  318.98 micro sec/call (100 elements, 100 keys) 

defaultdict:  714.70 micro sec/call (5,000 elements, 3 keys) 
defaultdict: 3892.92 micro sec/call (5,000 elements, 5,000 keys) 
    setdefault: 1502.91 micro sec/call (5,000 elements, 3 keys) 
    setdefault: 2888.08 micro sec/call (5,000 elements, 5,000 keys) 
OrderedDict: 3912.95 micro sec/call (5,000 elements, 3 keys) 
OrderedDict: 14863.02 micro sec/call (5,000 elements, 5,000 keys) 

defaultdict: 3649.02 micro sec/call (25,000 elements, 3 keys) 
defaultdict: 22313.17 micro sec/call (25,000 elements, 25,000 keys) 
    setdefault: 7447.28 micro sec/call (25,000 elements, 3 keys) 
    setdefault: 18426.88 micro sec/call (25,000 elements, 25,000 keys) 
OrderedDict: 19202.17 micro sec/call (25,000 elements, 3 keys) 
OrderedDict: 85946.45 micro sec/call (25,000 elements, 25,000 keys)