我希望先按照数字排序以下列表,然后按文字排序。
lst = ['b-3', 'a-2', 'c-4', 'd-2']
# result:
# ['a-2', 'd-2', 'b-3', 'c-4']
尝试1
res = sorted(lst, key=lambda x: (int(x.split('-')[1]), x.split('-')[0]))
我对此不满意,因为它需要将字符串拆分两次,以提取相关组件。
尝试2
我提出了以下解决方案。但我希望通过Pythonic lambda
语句提供更简洁的解决方案。
def sorter_func(x):
text, num = x.split('-')
return int(num), text
res = sorted(lst, key=sorter_func)
我查看Understanding nested lambda function behaviour in python但无法直接调整此解决方案。有没有更简洁的方法来重写上面的代码?
答案 0 :(得分:17)
有两点需要注意:
lambda
语句,因为函数组合不是标准库的一部分(请参阅注释#1)。您可以轻松完成的工作是让一个lambda
函数返回 另一个 lambda
函数的结果。因此,正确答案可以在Lambda inside lambda中找到。
针对您的具体问题,您可以使用:
res = sorted(lst, key=lambda x: (lambda y: (int(y[1]), y[0]))(x.split('-')))
请记住,lambda
只是一个功能。您可以在定义后立即调用它,即使是在同一行上。
注意#1 :第三方toolz
库允许合成:
from toolz import compose
res = sorted(lst, key=compose(lambda x: (int(x[1]), x[0]), lambda x: x.split('-')))
注意#2 :正如@chepner指出的那样,此解决方案的缺陷(重复函数调用)是考虑PEP-572的原因之一。
答案 1 :(得分: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']
答案 2 :(得分:2)
PeriodicWorkRequest
答案 3 :(得分:2)
在几乎所有情况下,我都会尝试第二次尝试。它是可读且简洁的(我希望每次使用三行而不是一行!)-尽管函数名可能更具描述性。但是,如果您将其用作本地函数,那就没什么大不了了。
您还必须记住,Python使用key
函数,而不是cmp
(比较)函数。因此,要对长度为n
的可迭代数组进行排序,key
函数将被精确调用n
次,但是排序通常会进行O(n * log(n))
个比较。因此,只要您的键函数的算法复杂度为O(1)
,键函数的调用开销就无关紧要了。那是因为:
O(n*log(n)) + O(n) == O(n*log(n))
有一个例外,这是Python sort
的最佳情况:在最佳情况下,sort
仅进行O(n)
比较,但这仅在可迭代对象已经排序(或几乎已排序)时发生排序)。如果Python具有比较功能(在Python 2中确实有一个比较功能),则该功能的常数会更为重要,因为它将被调用O(n * log(n))
次(每次比较均调用一次)。
因此,不要为简洁或提高速度而烦恼(除非您可以在不引入太大常数因素的情况下减少big-O,否则您应该这样做!),首先要考虑的是可读性。因此,您实际上应该不执行任何嵌套的lambda
或其他任何奇特的构造(可能作为练习除外)。
长话短说,只需使用您的#2:
def sorter_func(x):
text, num = x.split('-')
return int(num), text
res = sorted(lst, key=sorter_func)
顺便说一句,它也是所有提议的方法中最快的(尽管差别不大):
摘要:它可读且快速!
用于重现基准的代码。它需要安装simple_benchmark
才能运行(免责声明:这是我自己的库),但是可能有等效的框架可以执行这种任务,但我对此很熟悉:
# My specs: Windows 10, Python 3.6.6 (conda)
import toolz
import iteration_utilities as it
def approach_jpp_1(lst):
return sorted(lst, key=lambda x: (int(x.split('-')[1]), x.split('-')[0]))
def approach_jpp_2(lst):
def sorter_func(x):
text, num = x.split('-')
return int(num), text
return sorted(lst, key=sorter_func)
def jpp_nested_lambda(lst):
return sorted(lst, key=lambda x: (lambda y: (int(y[1]), y[0]))(x.split('-')))
def toolz_compose(lst):
return sorted(lst, key=toolz.compose(lambda x: (int(x[1]), x[0]), lambda x: x.split('-')))
def AshwiniChaudhary_list_comprehension(lst):
return sorted(lst, key=lambda x: [(int(num), text) for text, num in [x.split('-')]])
def AshwiniChaudhary_next(lst):
return sorted(lst, key=lambda x: next((int(num), text) for text, num in [x.split('-')]))
def PaulCornelius(lst):
return sorted(lst, key=lambda x: tuple(f(a) for f, a in zip((int, str), reversed(x.split('-')))))
def JeanFrançoisFabre(lst):
return sorted(lst, key=lambda s : [x if i else int(x) for i,x in enumerate(reversed(s.split("-")))])
def iteration_utilities_chained(lst):
return sorted(lst, key=it.chained(lambda x: x.split('-'), lambda x: (int(x[1]), x[0])))
from simple_benchmark import benchmark
import random
import string
funcs = [
approach_jpp_1, approach_jpp_2, jpp_nested_lambda, toolz_compose, AshwiniChaudhary_list_comprehension,
AshwiniChaudhary_next, PaulCornelius, JeanFrançoisFabre, iteration_utilities_chained
]
arguments = {2**i: ['-'.join([random.choice(string.ascii_lowercase),
str(random.randint(0, 2**(i-1)))])
for _ in range(2**i)]
for i in range(3, 15)}
b = benchmark(funcs, arguments, 'list size')
%matplotlib notebook
b.plot_difference_percentage(relative_to=approach_jpp_2)
我冒昧地包括了我自己的一个库iteration_utilities.chained
的函数组合方法:
from iteration_utilities import chained
sorted(lst, key=chained(lambda x: x.split('-'), lambda x: (int(x[1]), x[0])))
速度相当快(第2位或第3位),但比使用您自己的功能还慢。
请注意,如果您使用的算法具有key
(或更好)的算法复杂性,例如O(n)
或min
,则max
的开销会更大。那么按键功能的恒定因素会更显着!
答案 4 :(得分:1)
只有当项目的索引为0时(逆转拆分列表时),才可以转换为整数。创建的唯一对象(除split
的结果外)是用于比较的2元素列表。其余的只是迭代器。
sorted(lst,key = lambda s : [x if i else int(x) for i,x in enumerate(reversed(s.split("-")))])
顺便说一下,-
令牌在涉及数字时并不是特别好,因为它使负数的使用变得复杂(但可以用s.split("-",1)
来解决
答案 5 :(得分:0)
lst = ['b-3', 'a-2', 'c-4', 'd-2']
def xform(l):
return list(map(lambda x: x[1] + '-' + x[0], list(map(lambda x: x.split('-'), lst))))
lst = sorted(xform(lst))
print(xform(lst))
看到它here 我认为@jpp有一个更好的解决方案,但是一个有趣的小脑力激荡器: - )
答案 6 :(得分:0)
一般情况下,使用FOP(功能导向编程),您可以将它全部放在一个衬里中并将lambda嵌套在一行内,但这通常是不好的礼仪,因为在2嵌套功能之后它全部变为很不可思议。
解决此类问题的最佳方法是将其分为几个阶段:
1:将字符串拆分为tuple
:
lst = ['b-3', 'a-2', 'c-4', 'd-2']
res = map( lambda str_x: tuple( str_x.split('-') ) , lst)
2:按照您的意愿排序元素:
lst = ['b-3', 'a-2', 'c-4', 'd-2']
res = map( lambda str_x: tuple( str_x.split('-') ) , lst)
res = sorted( res, key=lambda x: ( int(x[1]), x[0] ) )
由于我们将字符串拆分为元组,因此它将返回一个地图对象,该对象将表示为元组列表。所以现在第3步是可选的:
3:代表您查询的数据:
lst = ['b-3', 'a-2', 'c-4', 'd-2']
res = map( lambda str_x: tuple( str_x.split('-') ) , lst)
res = sorted( res, key=lambda x: ( int(x[1]), x[0] ) )
res = map( '-'.join, res )
现在请记住,lambda nesting
可以生成更多的单行解决方案,并且您可以实际嵌入非离散嵌套类型的lambda,如下所示:
a = ['b-3', 'a-2', 'c-4', 'd-2']
resa = map( lambda x: x.split('-'), a)
resa = map( lambda x: ( int(x[1]),x[0]) , a)
# resa can be written as this, but you must be sure about type you are passing to lambda
resa = map( lambda x: tuple( map( lambda y: int(y) is y.isdigit() else y , x.split('-') ) , a)
但正如您可以看到list a
的内容除了由'-'
分隔的2个字符串类型之外的任何内容之外, lambda
函数都会引发错误,您将会我很难搞清楚到底发生了什么。
所以最后,我想向您展示第三步程序的几种编写方式:
1:
lst = ['b-3', 'a-2', 'c-4', 'd-2']
res = map( '-'.join,\
sorted(\
map( lambda str_x: tuple( str_x.split('-') ) , lst),\
key=lambda x: ( int(x[1]), x[0] )\
)\
)
2:
lst = ['b-3', 'a-2', 'c-4', 'd-2']
res = map( '-'.join,\
sorted( map( lambda str_x: tuple( str_x.split('-') ) , lst),\
key=lambda x: tuple( reversed( tuple(\
map( lambda y: int(y) if y.isdigit() else y ,x )\
)))\
)\
) # map isn't reversible
3:
res = sorted( lst,\
key=lambda x:\
tuple(reversed(\
tuple( \
map( lambda y: int(y) if y.isdigit() else y , x.split('-') )\
)\
))\
)
所以你可以看到这一切如何变得非常复杂和难以理解。在阅读我自己或别人的代码时,我常常喜欢看到这个版本:
res = map( lambda str_x: tuple( str_x.split('-') ) , lst) # splitting string
res = sorted( res, key=lambda x: ( int(x[1]), x[0] ) ) # sorting for each element of splitted string
res = map( '-'.join, res ) # rejoining string
这一切都来自我。玩得开心。我已在py 3.6
中测试了所有代码。
PS。一般来说,您有两种方法可以接近lambda functions
:
mult = lambda x: x*2
mu_add= lambda x: mult(x)+x #calling lambda from lambda
这种方式对于典型的FOP非常有用,在这种情况下,您拥有常量数据,并且需要操作该数据的每个元素。但是,如果您需要在 list,tuple,string,dict
中解决lambda
,则这些操作并非常有用,因为如果存在任何container/wrapper
类型,容器内元素的数据类型变得有问题。因此,我们需要提升抽象级别并确定如何根据其类型操作数据。
mult_i = lambda x: x*2 if isinstance(x,int) else 2 # some ternary operator to make our life easier by putting if statement in lambda
现在您可以使用其他类型的lambda
函数:
int_str = lambda x: ( lambda y: str(y) )(x)*x # a bit of complex, right?
# let me break it down.
#all this could be written as:
str_i = lambda x: str(x)
int_str = lambda x: str_i(x)*x
## we can separate another function inside function with ()
##because they can exclude interpreter to look at it first, then do the multiplication
# ( lambda x: str(x)) with this we've separated it as new definition of function
# ( lambda x: str(x) )(i) we called it and passed it i as argument.
有些人把这种语法称为嵌套的lambdas,我称之为indiscreet,因为你可以看到所有。
你可以使用递归的lambda赋值:
def rec_lambda( data, *arg_lambda ):
# filtering all parts of lambda functions parsed as arguments
arg_lambda = [ x for x in arg_lambda if type(x).__name__ == 'function' ]
# implementing first function in line
data = arg_lambda[0](data)
if arg_lambda[1:]: # if there are still elements in arg_lambda
return rec_lambda( data, *arg_lambda[1:] ) #call rec_lambda
else: # if arg_lambda is empty or []
return data # returns data
#where you can use it like this
a = rec_lambda( 'a', lambda x: x*2, str.upper, lambda x: (x,x), '-'.join)
>>> 'AA-AA'
答案 7 :(得分:-3)
我认为*如果你确定格式是一致的" [0]字母[1]破折号"超出[2:]的以下索引将始终为数字,然后您可以使用切片替换split,或者您可以使用str.index(' - ')
sorted(lst, key=lambda x:(int(x[2:]),x[0]))
# str.index('-')
sorted(lst, key=lambda x:(int(x[x.index('-')+1 :]),x[0]))