切片 Python OrderedDict
Slicing a Python OrderedDict
在我的代码中,我经常需要从 Python OrderedDict
(来自 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]
有没有更好的方法来完成这项工作?
在Python2中,你可以切片键:
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
标准库中的有序字典不提供该功能。尽管库在 collections.OrderedDict 之前已经存在了几年,具有此功能(并且本质上提供了 OrderedDict 的超集):voidspace odict and ruamel.ordereddict(我是后一个包的作者,它是 odict 的重新实现C):
from odict import OrderedDict as odict
p = odict([('a', 1), ('b', 2), ('c', 3), ('d', 4)])
print p[1:3]
在 ruamel.ordereddict 中你可以放宽输入顺序的要求(据我所知你不能问 dict 的派生词是否是有序的(对 ruamel.ordereddict 来说是很好的补充来识别 collection.OrderedDicts) ):
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
的 __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]
或者如果你只是想智能化所有现有的 OrderedDict
而不进行子类化:
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]
您可以使用 itertools.islice
函数,它接受一个可迭代对象并输出 stop
个第一个元素。这是有益的,因为可迭代对象不支持常见的切片方法,并且您不需要从 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)
我想使用键来切片,因为我事先不知道索引:
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)])
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)])
我能够使用以下方法对 OrderedDict 进行切片:
list(myordereddict.values())[start:stop]
我没有测试性能。
x = OrderedDict(o.items()[1:3])
在我的代码中,我经常需要从 Python OrderedDict
(来自 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]
有没有更好的方法来完成这项工作?
在Python2中,你可以切片键:
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
标准库中的有序字典不提供该功能。尽管库在 collections.OrderedDict 之前已经存在了几年,具有此功能(并且本质上提供了 OrderedDict 的超集):voidspace odict and ruamel.ordereddict(我是后一个包的作者,它是 odict 的重新实现C):
from odict import OrderedDict as odict
p = odict([('a', 1), ('b', 2), ('c', 3), ('d', 4)])
print p[1:3]
在 ruamel.ordereddict 中你可以放宽输入顺序的要求(据我所知你不能问 dict 的派生词是否是有序的(对 ruamel.ordereddict 来说是很好的补充来识别 collection.OrderedDicts) ):
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
的 __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]
或者如果你只是想智能化所有现有的 OrderedDict
而不进行子类化:
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]
您可以使用 itertools.islice
函数,它接受一个可迭代对象并输出 stop
个第一个元素。这是有益的,因为可迭代对象不支持常见的切片方法,并且您不需要从 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)
我想使用键来切片,因为我事先不知道索引:
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)])
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)])
我能够使用以下方法对 OrderedDict 进行切片:
list(myordereddict.values())[start:stop]
我没有测试性能。
x = OrderedDict(o.items()[1:3])