使用 operator.itemgetter() 作为排序键时,有没有办法转换值?
Posted
技术标签:
【中文标题】使用 operator.itemgetter() 作为排序键时,有没有办法转换值?【英文标题】:Is there a way to cast values when using operator.itemgetter() as sort key? 【发布时间】:2017-08-20 16:52:43 【问题描述】:我有一个包含字符串表示的数字的列表:
nums = [['1','3'],['2','2'],['1','2'],['0','2'],['11','2']]
我需要在不修改原始列表中数字的字符串表示的情况下,按第一个然后第二个条目对这些数字升序进行排序。另外,要避免创建另一个列表的第二个副本,其中所有内容都明确映射到整数——想象一下这是一个巨大的列表。
sort()
和 sorted()
都适用于元组和列表,因此使用 lambda 键,我可以执行以下操作:
>>> sorted(nums, key=lambda n: (int(n[0]),int(n[1]))
[['0', '2'], ['1', '2'], ['1', '3'], ['2', '2'], ['11', '2']]
快乐的日子...
但是,我看到许多关于使用 operator.itemgetter()
作为关键函数而不是使用 lambda 进行排序的帖子。 在不讨论这些声明的有效性的情况下,是否有人可以在使用 operator.itemgetter()
时应用从字符串到整数的转换以进行比较:
以下显然失败,因为字符串被比较为字符串,而不是数字:
>>> sorted(nums, key=operator.itemgetter(0,1))
[['0', '2'], ['1', '2'], ['1', '3'], ['11', '2'], ['2', '2']]
【问题讨论】:
Yeeesssss.... 但它会比等效的幼稚 lambda 长得多。itemgetter
如果您要获得所有项,则不是很有用。
您调用了列表nums
,但它不包含任何数字,仅包含列表和字符串。除非排序是这些字符串的数值唯一重要并且它们在所有其他时间都需要是字符串,否则您可以将它们转换为原始列表中的整数,然后您不需要传递排序键。跨度>
@TigerhawkT3 忘了添加那个细节,很好。我无法修改现有列表——它们需要保留字符串。只希望它们像整数一样排序。更新问题文本以澄清这一点。
包含表示数字的字符串的列表是一个奇怪的数据。数据何时/何地进入您的应用程序时转换为数字。
【参考方案1】:
operator.itemgetter
快不是因为它在sort
中做了什么特别的事情,而是因为它完全是written in c,并且不涉及调用python 函数。
所以您正在寻找的是一个 C 函数,它可以满足您的需求 - itemgetter
是一个红鲱鱼。
在 python 2 中,您可以避免使用 key=functools.partial(map, int)
调用纯 python 函数。这在 python 3 中不起作用,因为 map
不再返回列表或元组。这也可能不会比您的解决方案快。
【讨论】:
【参考方案2】:有办法,比如使用iteration_utilities.chained
1和functools.partial
:
>>> import operator import itemgetter
>>> from iteration_utilities import chained
>>> from functools import partial
>>> itemgetter_int = chained(operator.itemgetter(0, 1), partial(map, int), tuple)
>>> sorted(nums, key=itemgetter_int)
[['0', '2'], ['1', '2'], ['1', '3'], ['2', '2'], ['11', '2']]
它可以工作,但肯定比使用 lambda
或自定义函数慢。
如果您真的需要速度,您可以对 lambda
函数进行 cythonize(或用 C 手动编写),但如果您只需要在一个地方使用它,只需使用一次性的 lambda
。特别是如果它在sorted
中,因为它有O(nlog(n))
比较,所以O(n)
函数调用可能对整体执行时间贡献不大。
1:这是我编写的3rd party extension module 中的一个函数。它需要单独安装,例如通过conda
或pip
。
【讨论】:
@Ray,那么您运行的代码与他发布的代码不同。 “iteration_utilities”是第 3 方扩展:pypi.python.org/pypi/iteration_utilities @Ray 这个链式只是一个函数组合,如果你不想要它的第三方版本,你可以像from functools import partial, reduce;from operator import itemgetter
那样做,并把它们用作itemgetter_int = partial(reduce, lambda x,f:f(x), [itemgetter(0,1), partial(map,int),tuple])
@TimPeters 你确实是正确的。通过 pip 安装的 iterations_utilities 现在可以按预期工作了。
@Copperfield 那么使用reduce
和lambda
来避免更简单的key=lambda...
会破坏目的,你不觉得吗? :)
为什么不只是chained(partial(map, int), tuple)
? itemgetter 是无操作的。【参考方案3】:
4 年后,这是我认为最符合问题精神的解决方案:
>>> import operator
>>> ig = operator.itemgetter(0,1)
>>> nums = [['1', '3'], ['2', '2'], ['1', '2'], ['0', '2'], ['11', '2']]
>>> sorted( nums,key = lambda x : tuple(map(int,ig(x))) )
[['0', '2'], ['1', '2'], ['1', '3'], ['2', '2'], ['11', '2']]
【讨论】:
以上是关于使用 operator.itemgetter() 作为排序键时,有没有办法转换值?的主要内容,如果未能解决你的问题,请参考以下文章
使用 operator.itemgetter() 作为排序键时,有没有办法转换值?