【问题标题】:Last element in OrderedDictOrderedDict 中的最后一个元素
【发布时间】:2012-04-12 14:46:56
【问题描述】:

我有 od 类型的 OrderedDict。我想访问它最近添加的 (key, value) 对。 od.popitem(last = True) 会这样做,但也会从我不想要的 od 中删除这对。

有什么好的方法来做到这一点?我可以/应该这样做吗:

class MyOrderedDict(OrderedDict):
  def last(self):
    return next(reversed(self))

【问题讨论】:

    标签: python python-3.x ordereddictionary


    【解决方案1】:

    使用next(reversed(od)) 是访问最近添加的元素的完美方式。 OrderedDict 类对字典项使用双向链表并实现 __reversed__(),因此此实现使您可以 O(1) 访问所需的元素。是否值得为这个简单的操作子类化OrderedDict() 可能会受到质疑,但这种方法实际上并没有错。

    【讨论】:

    • @hobs:是的,它只给你钥匙。如何获得给定键的值留给读者作为练习。 :)
    • FWIW, O(2) 和 O(1) 渐近等价
    • 在 Python 3.5 中,可以通过 next(reversed(od.values())) 获取最后一个值
    • 还要注意,如果你想要 first 元素,你应该使用next(iter(od))
    • 难怪在 Python 3.5 中你可以通过 next(reversed(od.items())) 得到最后一个 (key, value) 对。
    【解决方案2】:

    来自 timeit 的一点魔法可以在这里提供帮助......

    from collections import OrderedDict
    class MyOrderedDict1(OrderedDict):
      def last(self):
        k=next(reversed(self))
        return (k,self[k])
    
    class MyOrderedDict2(OrderedDict):
      def last(self):
         out=self.popitem()
         self[out[0]]=out[1]
         return out
    
    class MyOrderedDict3(OrderedDict):
      def last(self):
         k=(list(self.keys()))[-1]
         return (k,self[k])
    
    if __name__ == "__main__":
      from timeit import Timer
    
      N=100
    
      d1=MyOrderedDict1()
      for i in range(N): d1[i]=i
    
      print ("d1",d1.last())
    
      d2=MyOrderedDict2()
      for i in range(N): d2[i]=i
    
      print ("d2",d2.last())
    
      d3=MyOrderedDict3()
      for i in range(N): d3[i]=i
    
      print("d3",d3.last())
    
    
    
      t=Timer("d1.last()",'from __main__ import d1')
      print ("OrderedDict1",t.timeit())
      t=Timer("d2.last()",'from __main__ import d2')
      print ("OrderedDict2",t.timeit())
      t=Timer("d3.last()",'from __main__ import d3')
      print ("OrderedDict3",t.timeit())
    

    结果:

    d1 (99, 99)
    d2 (99, 99)
    d3 (99, 99)
    OrderedDict1 1.159217119216919
    OrderedDict2 3.3667118549346924
    OrderedDict3 24.030261993408203
    

    (在 python3.2、Ubuntu Linux 上测试)。

    正如@SvenMarnach 所指出的,与我可以烹饪的其他两种方法相比,您描述的方法非常有效。

    【讨论】:

      【解决方案3】:

      上帝,我希望这都是内置功能...

      这里有一些东西可以节省您宝贵的时间。在 Python 3.7 中测试。 od 是您的 OrderedDict。

      
      # Get first key
      next(iter(od))
      
      # Get last key
      next(reversed(od))
      
      # Get first value
      od[next(iter(od))]
      
      # Get last value
      od[next(reversed(od))]
      
      # Get first key-value tuple
      next(iter(od.items()))
      
      # Get last key-value tuple
      next(reversed(od.items()))
      

      【讨论】:

      • 很好的例子,只有一个观察,keys方法不是必需的
      • 所有这些操作都是O(1)吗?
      • @KnightKnight 因为不涉及排序或比较,并且鉴于 Python 的大多数收集方法应该是惰性的,我敢打赌是的。但不要在采访中相信我的话。
      【解决方案4】:

      您的想法很好,但是默认迭代器仅在键上,因此您的示例将仅返回最后一个键。你真正想要的是:

      class MyOrderedDict(OrderedDict):
          def last(self):
              return list(self.items())[-1]
      

      这会提供 (key, value) 对,而不仅仅是您想要的键。

      请注意,在 Python 3.x 之前的版本中,OrderedDict.items() 返回一个列表,因此您不需要调用 list(),但更高版本会返回一个 dictionary view object,所以您会这样做。

      编辑:如 cmets 中所述,更快的操作是:

      class MyOrderedDict(OrderedDict):
          def last(self):
              key = next(reversed(self))
              return (key, self[key])
      

      虽然我必须承认我确实在代码中发现了这个更丑陋的东西(我从不喜欢获取密钥然后执行 x[key] 来单独获取值,我更喜欢获取 (key, value) 元组) - 取决于速度和您的偏好,您可能希望选择前一个选项。

      【讨论】:

      • 当然,这将是一个 O(n) 操作。最好使用原始实现来获取键,并通过普通的字典访问来获取值。 (当你无论如何都必须建立一个列表时,[-1]next(reversed(...)) 容易得多。)
      • 你说得很好,已更正。太容易陷入解决问题的一种方式。
      猜你喜欢
      • 2017-11-28
      • 1970-01-01
      • 2018-06-16
      • 1970-01-01
      • 2012-04-02
      • 2013-02-06
      • 2012-04-08
      • 2016-10-13
      • 1970-01-01
      相关资源
      最近更新 更多