web-dev-qa-db-ja.com

スライスa Python OrderedDict

私のコードでは、キーと値のサブセット範囲をPython OrderedDict(from collections パッケージ)から取得する必要があります) 。スライスが機能しません(スローTypeError: unhashable type)そして、代替の反復は面倒です:

from collections import OrderedDict

o = OrderedDict([('a', 1), ('b', 2), ('c', 3), ('d', 4)])

# want to do:
# x = o[1:3]
# need to do:
x = OrderedDict()
for idx, key in enumerate(o):
    if 1 <= idx < 3:
        x[key] = o[key]

これを行うためのより良い方法はありますか?

16
user5035297

標準ライブラリの順序付けられたdictは、その機能を提供しません。この機能を備えた(そして本質的にOrderedDictのスーパーセットを提供する)collections.OrderedDictの前にライブラリが数年間存在していましたが、 voidspace odict および ruamel.ordereddict (私はC)のodictの再実装である後者のパッケージの作者:

from odict import OrderedDict as odict
p = odict([('a', 1), ('b', 2), ('c', 3), ('d', 4)])
print p[1:3]

Ruamel.ordereddictでは、順序付けされた入力要件を緩和できます(AFAIKでは、キーが順序付けられているかどうかdictの導関数を要求することはできません(collection.OrderedDictsを認識するためにruamel.ordereddictに追加するとよいでしょう)):

from ruamel.ordereddict import ordereddict

q = ordereddict(o, relax=True)
print q[1:3]
r = odict([('a', 1), ('b', 2), ('c', 3), ('d', 4)])
print r[1:3]

標準ライブラリ内にとどまりたい(またはしなければならない)場合は、サブラスcollections.OrderedDict's __getitem__

class SlicableOrderedDict(OrderedDict):
    def __getitem__(self, k):
        if not isinstance(k, slice):
            return OrderedDict.__getitem__(self, k)
        x = SlicableOrderedDict()
        for idx, key in enumerate(self.keys()):
            if k.start <= idx < k.stop:
                x[key] = self[key]
        return x

s = SlicableOrderedDict([('a', 1), ('b', 2), ('c', 3), ('d', 4)])
print s[1:3]

もちろん、MartijnまたはJimmyの短いバージョンを使用して、返す必要のある実際のスライスを取得できます。

from itertools import islice
class SlicableOrderedDict(OrderedDict):
    def __getitem__(self, k):
        if not isinstance(k, slice):
            return OrderedDict.__getitem__(self, k)
        return SlicableOrderedDict(islice(self.viewitems(), k.start, k.stop))

t = SlicableOrderedDict([('a', 1), ('b', 2), ('c', 3), ('d', 4)])
print t[1:3]

または、サブクラス化せずに既存のすべてのOrderedDictsをスマート化するだけの場合:

def get_item(self, k):
    if not isinstance(k, slice):
        return OrderedDict._old__getitem__(self, k)
    return OrderedDict(islice(self.viewitems(), k.start, k.stop))

OrderedDict._old__getitem__ = OrderedDict.__getitem__
OrderedDict.__getitem__ = get_item

u = OrderedDict([('a', 1), ('b', 2), ('c', 3), ('d', 4)])
print u[1:3]
14
Anthon

itertools.islice関数を使用できます。この関数は、反復可能で、stopの最初の要素を出力します。 iterablesは一般的なスライス方法をサポートしておらず、OrderedDictからitemsリスト全体を作成する必要がないため、これは有益です。

from collections import OrderedDict
from itertools import islice
o = OrderedDict([('a', 1), ('b', 2), ('c', 3), ('d', 4)])
sliced = islice(o.iteritems(), 3)  # o.iteritems() is o.items() in Python 3
sliced_o = OrderedDict(sliced)
15
Jimmy C

Python 2では、キーをスライスできます:

_x.keys()[1:3]
_

そして、Python 2とPython 3の両方をサポートするには、最初にリストに変換します。

_list(k)[1:3]
_

Python 2 OrderedDict.keys()実装はまさにそれを行います。

どちらの場合も、正しい順序でキーのリストが表示されます。最初にリスト全体を作成することが問題になる場合は、 itertools.islice() を使用して、生成される反復可能オブジェクトをリストに変換できます。

_from itertools import islice

list(islice(x, 1, 3))
_

上記のすべては、アイテムにも適用できます。 Python 2でdict.viewitems()を使用して、Python 3 dict.items()が提供するのと同じ反復動作を取得します。渡すことができますこの場合、islice()オブジェクトは別のOrderedDict()に直接接続されます。

_OrderedDict(islice(x.items(), 1, 3))  # x.viewitems() in Python 2
_
5
Martijn Pieters
def slice_odict(odict, start=None, end=None):
    return OrderedDict([
        (k,v) for (k,v) in odict.items() 
        if k in list(odict.keys())[start:end]
    ])

これにより、次のことが可能になります。

>>> x = OrderedDict([('a',1), ('b',2), ('c',3), ('d',4)])
>>> slice_odict(x, start=-1)
OrderedDict([('d', 4)])
>>> slice_odict(x, end=-1)
OrderedDict([('a', 1), ('b', 2), ('c', 3)])
>>> slice_odict(x, start=1, end=3)
OrderedDict([('b', 2), ('c', 3)])
0
alukach

事前にインデックスを知らなかったので、キーを使用してスライスしたかった:

o = OrderedDict(Zip(list('abcdefghijklmnopqrstuvwxyz'),range(1,27)))

stop = o.keys().index('e')           # -> 4
OrderedDict(islice(o.items(),stop))  # -> OrderedDict([('a', 1), ('b', 2), ('c', 3)])

またはstartからstopにスライスするには:

start = o.keys().index('c')                    # -> 2
stop = o.keys().index('e')                     # -> 4
OrderedDict(islice(o.iteritems(),start,stop))  # -> OrderedDict([('c', 3), ('d', 4)])
0
Stew