【问题标题】:Slicing a Python OrderedDict切片 Python OrderedDict
【发布时间】:2015-09-07 15:10:37
【问题描述】:

在我的代码中,我经常需要从 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]

有没有更好的方法来完成这项工作?

【问题讨论】:

    标签: python-2.7 slice ordereddictionary


    【解决方案1】:
    x = OrderedDict(o.items()[1:3])
    

    【讨论】:

    【解决方案2】:

    我能够使用以下命令对 OrderedDict 进行切片:

    list(myordereddict.values())[start:stop]
    

    我没有测试性能。

    【讨论】:

      【解决方案3】:
      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)])
      

      【讨论】:

        【解决方案4】:

        标准库中的有序字典不提供该功能。即使库在 collections.OrderedDict 之前已经存在了几年,它具有此功能(并且本质上提供了 OrderedDict 的超集):voidspace odictruamel.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 中,您可以放宽有序的输入要求(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__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]
        

        【讨论】:

        • 这很有帮助,谢谢!请注意,SlicableOrderedDict 不支持所写的负索引。
        • 使用 odict 包时出现错误:'TypeError: unhashable type: 'slice'
        • 此外,更改内置 getitem 实现(不进行子类化)会导致 'TypeError: can't set attributes of built-in/extension type 'collections.OrderedDict' (对于子类化示例,对于 python 3.x,将 self.viewitems() 更改为 self.items())
        【解决方案5】:

        我想使用键进行切片,因为我事先不知道索引:

        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)])
        

        【讨论】:

          【解决方案6】:

          在 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
          

          【讨论】:

            【解决方案7】:

            您可以使用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(n) 吗?看来这个操作没必要。
            • 在 Python 3.7 中,我收到错误 collections.OrderedDict' object has no attribute 'iteritems
            • 在 Python 3 中使用 o.items()
            猜你喜欢
            • 2021-04-06
            • 2021-02-19
            • 2021-11-26
            • 2015-03-10
            • 2013-05-09
            • 2016-12-26
            • 2012-07-05
            • 2023-04-04
            • 2011-09-08
            相关资源
            最近更新 更多