排序列表时嵌套的lambda语句

jpp*_*jpp 13 python sorting lambda list python-3.x

我希望首先按数字排序以下列表,然后按文字排序.

lst = ['b-3', 'a-2', 'c-4', 'd-2']

# result:
# ['a-2', 'd-2', 'b-3', 'c-4']
Run Code Online (Sandbox Code Playgroud)

尝试1

res = sorted(lst, key=lambda x: (int(x.split('-')[1]), x.split('-')[0]))
Run Code Online (Sandbox Code Playgroud)

我对此不满意,因为它需要将字符串拆分两次,以提取相关组件.

尝试2

我提出了以下解决方案.但我希望通过Pythonic lambda声明有一个更简洁的解决方案.

def sorter_func(x):
    text, num = x.split('-')
    return int(num), text

res = sorted(lst, key=sorter_func)
Run Code Online (Sandbox Code Playgroud)

在python中查看了理解嵌套的lambda函数行为,但无法直接调整此解决方案.有没有更简洁的方法来重写上面的代码?

jpp*_*jpp 17

有两点需要注意:

  • 单行答案不一定更好.使用命名函数可能会使您的代码更易于阅读.
  • 您可能不会查找嵌套lambda语句,因为函数组合不是标准库的一部分(请参阅注释#1).你可以轻松做到的是有一个lambda函数返回另一个 lambda函数的结果.

因此,Lambda内部的Lambda中可以找到正确的答案.

对于您的具体问题,您可以使用:

res = sorted(lst, key=lambda x: (lambda y: (int(y[1]), y[0]))(x.split('-')))
Run Code Online (Sandbox Code Playgroud)

请记住,这lambda只是一个功能.您可以在定义它后立即调用它,即使在同一行上也是如此.

注意#1:第三方toolz库允许合成:

from toolz import compose

res = sorted(lst, key=compose(lambda x: (int(x[1]), x[0]), lambda x: x.split('-')))
Run Code Online (Sandbox Code Playgroud)

注意#2:正如@chepner指出的那样,这个解决方案的缺陷(重复的函数调用)是考虑PEP-572的原因之一.


Ash*_*ary 6

我们可以包装split('-')在另一个列表下返回的列表,然后我们可以使用循环来处理它:

# Using list-comprehension
>>> sorted(lst, key=lambda x: [(int(num), text) for text, num in [x.split('-')]])
['a-2', 'd-2', 'b-3', 'c-4']
# Using next()
>>> sorted(lst, key=lambda x: next((int(num), text) for text, num in [x.split('-')]))
['a-2', 'd-2', 'b-3', 'c-4']
Run Code Online (Sandbox Code Playgroud)


MSe*_*ert 3

在几乎所有情况下,我都会简单地接受你的第二次尝试。它可读且简洁(我每次都更喜欢三行简单的行而不是一行复杂的行!) - 尽管函数名称可能更具描述性。但如果你将它用作本地函数,那就没什么大不了的了。

\n\n

您还必须记住,Python 使用的是key函数,而不是cmp(比较)函数。因此,要对长度的可迭代对象进行排序,nkey函数会被精确调用n几次,但排序通常会进行O(n * log(n))比较。因此,每当您的关键函数具有算法复杂性时,O(1)关键函数调用开销就不会产生太大影响。那是因为:

\n\n
O(n*log(n)) + O(n)   ==  O(n*log(n))\n
Run Code Online (Sandbox Code Playgroud)\n\n

有一个例外,这是 Python 的最佳情况sort:在最好的情况下,sort只会进行O(n)比较,但只有当可迭代对象已经排序(或几乎排序)时才会发生这种情况。如果 Python 有一个比较函数(在 Python 2 中确实有一个),那么该函数的常数因子将更加重要,因为它将被调用O(n * log(n))一次(每次比较调用一次)。

\n\n

因此,不要费心让它变得更简洁或更快(除非你可以减少大 O 而不引入太大的常数因子 - 那么你应该追求它!),首先考虑的应该是可读性。所以你真的应该做任何嵌套lambda或任何其他花哨的构造(除了作为练习)。

\n\n

长话短说,只需使用你的#2:

\n\n
def sorter_func(x):\n    text, num = x.split(\'-\')\n    return int(num), text\n\nres = sorted(lst, key=sorter_func)\n
Run Code Online (Sandbox Code Playgroud)\n\n

顺便说一句,它也是所有提议的方法中最快的(尽管差别不大):

\n\n

在此输入图像描述

\n\n

摘要:它可读且速度快

\n\n

重现基准测试的代码。需要simple_benchmark安装它才能工作(免责声明:这是我自己的库),但可能有等效的框架来完成此类任务,但我只是熟悉它:

\n\n
# My specs: Windows 10, Python 3.6.6 (conda)\n\nimport toolz\nimport iteration_utilities as it\n\ndef approach_jpp_1(lst):\n    return sorted(lst, key=lambda x: (int(x.split(\'-\')[1]), x.split(\'-\')[0]))\n\ndef approach_jpp_2(lst):\n    def sorter_func(x):\n        text, num = x.split(\'-\')\n        return int(num), text\n    return sorted(lst, key=sorter_func)\n\ndef jpp_nested_lambda(lst):\n    return sorted(lst, key=lambda x: (lambda y: (int(y[1]), y[0]))(x.split(\'-\')))\n\ndef toolz_compose(lst):\n    return sorted(lst, key=toolz.compose(lambda x: (int(x[1]), x[0]), lambda x: x.split(\'-\')))\n\ndef AshwiniChaudhary_list_comprehension(lst):\n    return sorted(lst, key=lambda x: [(int(num), text) for text, num in [x.split(\'-\')]])\n\ndef AshwiniChaudhary_next(lst):\n    return sorted(lst, key=lambda x: next((int(num), text) for text, num in [x.split(\'-\')]))\n\ndef PaulCornelius(lst):\n    return sorted(lst, key=lambda x: tuple(f(a) for f, a in zip((int, str), reversed(x.split(\'-\')))))\n\ndef JeanFran\xc3\xa7oisFabre(lst):\n    return sorted(lst, key=lambda s : [x if i else int(x) for i,x in enumerate(reversed(s.split("-")))])\n\ndef iteration_utilities_chained(lst):\n    return sorted(lst, key=it.chained(lambda x: x.split(\'-\'), lambda x: (int(x[1]), x[0])))\n\nfrom simple_benchmark import benchmark\nimport random\nimport string\n\nfuncs = [\n    approach_jpp_1, approach_jpp_2, jpp_nested_lambda, toolz_compose, AshwiniChaudhary_list_comprehension,\n    AshwiniChaudhary_next, PaulCornelius, JeanFran\xc3\xa7oisFabre, iteration_utilities_chained\n]\n\narguments = {2**i: [\'-\'.join([random.choice(string.ascii_lowercase),\n                              str(random.randint(0, 2**(i-1)))]) \n                    for _ in range(2**i)] \n             for i in range(3, 15)}\n\nb = benchmark(funcs, arguments, \'list size\')\n\n%matplotlib notebook\nb.plot_difference_percentage(relative_to=approach_jpp_2)\n
Run Code Online (Sandbox Code Playgroud)\n\n

我冒昧地包含了我自己的一个库的函数组合方法iteration_utilities.chained

\n\n
from iteration_utilities import chained\nsorted(lst, key=chained(lambda x: x.split(\'-\'), lambda x: (int(x[1]), x[0])))\n
Run Code Online (Sandbox Code Playgroud)\n\n

它相当快(第二或第三名),但仍然比使用您自己的函数慢。

\n\n
\n\n

请注意,如果您使用具有(或更好)算法复杂性的函数(例如或 ) ,key开销会更大。那么关键函数的常数因子就更显着了!O(n)minmax

\n