【问题标题】:Sorting dictionary using operator.itemgetter使用 operator.itemgetter 对字典进行排序
【发布时间】:2011-06-09 02:12:09
【问题描述】:

A question was asked here on SO,几分钟前,关于根据值对字典键进行排序。

几天前我刚读到operator.itemgetter 的排序方法,并决定尝试一下,但它似乎不起作用。

并不是说我对问题的答案有任何问题,我只是想用operator.itemgetter 试试这个。

所以字典是:

>>> mydict = { 'a1': ['g',6],
           'a2': ['e',2],
           'a3': ['h',3],
           'a4': ['s',2],
           'a5': ['j',9],
           'a6': ['y',7] }

我试过这个:

>>> l = sorted(mydict.itervalues(), key=operator.itemgetter(1))
>>> l
[['e', 2], ['s', 2], ['h', 3], ['g', 6], ['y', 7], ['j', 9]]

这可以按我的意愿工作。但是,由于我没有完整的字典 (mydict.itervalues()),我尝试了这个:

>>> complete = sorted(mydict.iteritems(), key=operator.itemgetter(2))

这不起作用(如我所料)。

那么我如何使用operator.itemgetter 对字典进行排序并在嵌套的键值对上调用itemgetter

【问题讨论】:

    标签: python sorting dictionary


    【解决方案1】:
    In [6]: sorted(mydict.iteritems(), key=lambda (k,v): operator.itemgetter(1)(v))
    Out[6]: 
    [('a2', ['e', 2]),
     ('a4', ['s', 2]),
     ('a3', ['h', 3]),
     ('a1', ['g', 6]),
     ('a6', ['y', 7]),
     ('a5', ['j', 9])]
    

    关键参数始终是一个函数,它一次从可迭代 (mydict.iteritems()) 中提供一个项目。在这种情况下,项目可能类似于

    ('a2',['e',2])
    

    所以我们需要一个可以将('a2',['e',2])作为输入并返回2的函数。

    lambda (k,v): ... 是一个匿名函数,它接受一个参数 - 一个 2 元组 - 并将其解压缩为 kv。因此,当 lambda 函数应用于我们的项目时,k 将是 'a2'v 将是 ['e',2]

    lambda (k,v): operator.itemgetter(1)(v) 应用于我们的项目,因此返回 operator.itemgetter(1)(['e',2]),它“获取”['e',2] 中的第二项,即 2。

    请注意,lambda (k,v): operator.itemgetter(1)(v) 不是用 Python 编写代码的好方法。正如 gnibbler 指出的那样,operator.itemgetter(1) 会为每个项目重新计算。那是低效的。使用operator.itemgetter(1) 的目的是创建一个可以多次应用的函数。您不想每次都重新创建函数。 lambda (k,v): v[1] 可读性更强,速度更快:

    In [15]: %timeit sorted(mydict.iteritems(), key=lambda (k,v): v[1])
    100000 loops, best of 3: 7.55 us per loop
    
    In [16]: %timeit sorted(mydict.iteritems(), key=lambda (k,v): operator.itemgetter(1)(v))
    100000 loops, best of 3: 11.2 us per loop
    

    【讨论】:

    • 你能解释一下吗:operator.itemgetter(1)(v))
    • @A A:operator.itemgetter(1)(v) 等价于 v[1]。您可以将任何出现的a[b] 替换为operator.itemgetter(b)(a),但这不是 理解您的问题的方式:)
    • 创建一个itemgetter 是相对昂贵的。这会为字典中的每个键创建一个
    • 对于 python3 使用项目而不是 iteritems
    • 语法无效,因为lambda 不接受元组。
    【解决方案2】:

    答案是——你不能。 operator.itemgetter(i) 返回一个可调用项,该可调用项返回其参数的 i 项,即

    f = operator.itemgetter(i)
    f(d) == d[i]
    

    它永远不会返回像d[i][j] 这样的东西。如果你真的想以纯函数式的方式来做这件事,你可以编写自己的 compose() 函数:

    def compose(f, g):
        return lambda *args: f(g(*args))
    

    并使用

    sorted(mydict.iteritems(), key=compose(operator.itemgetter(1),
                                           operator.itemgetter(1)))
    

    请注意,我不建议这样做:)

    【讨论】:

    • unutbu 有一个解决方案,虽然还不清楚。我们会等他更新。
    【解决方案3】:

    itemgetter 不支持嵌套(虽然 attrgetter 支持)

    你需要像这样展平字典

    sorted(([k]+v for k,v in mydict.iteritems()), key=itemgetter(2))
    

    【讨论】:

      【解决方案4】:

      通常以kv[1][1] 为索引更快:

      >>> from timeit import timeit
      >>> setup = 'import operator; g = operator.itemgetter(1); '
      >>> setup += 'd = {i: list(range(i+2)) for i in range(100)}'
      >>> kwargs = {'setup': setup, 'number': 10000}
      
      >>> timeit('sorted(d.items(), key=lambda kv: kv[1][1])', **kwargs)
      0.5251589557155967
      
      >>> timeit('sorted(d.items(), key=lambda kv: g(kv[1]))', **kwargs)
      0.7175205536186695
      
      >>> timeit('sorted(d.items(), key=lambda kv: g(kv)[1])', **kwargs)
      0.7915238151326776
      
      >>> timeit('sorted(d.items(), key=lambda kv: g(g(kv)))', **kwargs)
      0.9781978335231543
      

      【讨论】:

        【解决方案5】:

        在 Python 3 中移除了解压元组参数的功能:请参阅 PEP 3113

        因此,接受的答案不适用于运行 python 3.x 的人:

        等效的解决方案是

        sorted(mydict.items(),key=lambda kv:operator.itemgetter(1)(kv[1]) )
        

        【讨论】:

          猜你喜欢
          • 2018-01-26
          • 1970-01-01
          • 2014-05-26
          • 1970-01-01
          • 1970-01-01
          • 2021-03-24
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多