Iwa*_*993 2 python sorting python-3.x
我需要使用 python 3 对列表进行排序。可能有strings、integers、floats、 或tuples等。
我目前正在尝试sort使用如下key参数正确使用该函数:
data.sort(key=gen_key)
...
def gen_key(self, value):
if is_number(value):
return str(value)
if isinstance(value, str):
return value
return '___' + type(value).__name__
Run Code Online (Sandbox Code Playgroud)
但问题是数字现在将自然排序。虽然我想对数字和浮点数进行排序,但仍然像数字和浮点数一样,而不是将它们视为字符串。
该行为是由return str(value)零件引起的。但是我不能返回与字符串不同的类型,因为这会引发异常,因为从 python 3 开始,字符串不会像在 python 2 中那样用数字排序。异常如下
unordarable types: int() < str()
Run Code Online (Sandbox Code Playgroud)
有什么建议?
诀窍是让您的key函数返回一个元组,在第一个索引中具有保证的可比较类型,并在后续索引中返回不同的类型。
虽然与 Python 2 的功能并非 100% 相同,但对于“数字在前面,其他所有内容按类型名进行比较”的特定情况,您可以使用一个相当有效的key函数来做到这一点:
>>> from numbers import Number
>>> seq = ['Z', 3, 'Y', 1, 'X', 2.5, False, (1, 2), [2, 3], None]
>>> sorted(seq, key=lambda x: (x is not None, '' if isinstance(x, Number) else type(x).__name__, x))
[None, False, 1, 2.5, 3, [2, 3], 'X', 'Y', 'Z', (1, 2)]
Run Code Online (Sandbox Code Playgroud)
key这里的函数使 a 的第一个元素变得key简单bool,强制None在其他所有元素之前进行排序(Py2 做了同样的事情),然后首先使用空字符串作为键的第二部分对所有数字类型进行排序,其他所有元素都使用它们的类型名称(也像 Py2)。一旦你通过了前两个索引,剩下的就是相同的类型,应该比较好。
这里的主要缺陷是,可比的非数字类型,如set并frozenset不会相互比较,他们会通过类型名称只(自定义键类使用异常可以处理此)进行排序。
它也不会处理递归情况;如果序列包含[2, 3]and ['a', 'b'],它将与TypeError进行比较,但除了一个荒谬的关键类之外,没有什么可以处理的。2'a'
如果这不是问题,那么运行起来很便宜并且相对简单。
与涉及__lt__定义来执行比较的自定义类的解决方案不同,这种方法具有生成内置键的优势,这与排序期间 Python 级代码的最少执行有效地进行比较。
时间:
# Multiply out the sequence so log n factor in n log n work counts for something
>>> seq = ['Z', 3, 'Y', 1, 'X', 2.5, False, (1, 2), [2, 3], None] * 100
# Verify equivalence
>>> sorted(seq, key=Py2Key) == sorted(seq, key=lambda x: (x is not None, '' if isinstance(x, Number) else type(x).__name__, x))
True
# Timings in seconds for the fastest time (of 3 trials) to run the sort 1000 times:
>>> import timeit
# Py2Key class
>>> min(timeit.repeat('sorted(seq, key=Py2Key)', 'from __main__ import seq, Py2Key', number=1000))
5.251885865057375
>>> min(timeit.repeat('sorted(seq, key=lambda x: (x is not None, "" if isinstance(x, Number) else type(x).__name__, x))', 'from __main__ import seq, Number', number=1000))
1.9556877178131344
Run Code Online (Sandbox Code Playgroud)
基本上,避免动态 Python 级别的开销__lt__将运行时间减少了 60% 以上。这似乎不是算法上的改进(seq100 倍长具有相同的运行时间比率),只是固定开销的减少,但这是一个非平凡的减少。