将Python列表排序,使字母排在数字之前

8

我很新于Python,希望能够找到一种将单词排在数字之前的列表排序方法。

我了解可以使用sort进行以下操作:

a = ['c', 'b', 'd', 'a']
a.sort()
print(a)
['a', 'b', 'c', 'd']

b = [4, 2, 1, 3]
b.sort()
print(b)
[1, 2, 3, 4]

c = ['c', 'b', 'd', 'a', 4, 2, 1, 3]
c.sort()
print(c)
[1, 2, 3, 4, 'a', 'b', 'c', 'd']

然而,我想对c进行排序以获得:

['a', 'b', 'c', 'd', 1, 2, 3, 4]

先行致谢


5
警告:你的第三个示例在Python 3中无法正常工作。相反,你将会得到TypeError: unorderable types: int() < str()的错误提示。 - Kevin
6个回答

11

您可以提供一个自定义的 key 参数,使字符串比整数的权重更低:

>>> c = ['c', 'b', 'd', 'a', 4, 2, 1, 3]
>>> c.sort(key = lambda item: ([str,int].index(type(item)), item))
>>> c
['a', 'b', 'c', 'd', 1, 2, 3, 4]

2
[0, 1][type(item) == int] - thefourtheye
如果列表中存在除了 strint 以外的类型,则会出现 IndexError 错误。 - dawg
@dawg,没错。如果你想要更多的类型,可以将它们添加到 [str, int] 列表中。或者你甚至可以用 lambda item: ({str:0, int:1}.get(type(item), 2), item) 替换它,让所有非字符串和非整数都排在右边。 - Kevin
2
这不符合Python的多态性原则:你应该允许任何像int一样的对象被视为int来处理,而不是通过使用“type”强制要求它成为一个int -- 而是应该使用“isinstance”来保持派生类的一致性。 - the wolf

5

默认的Python排序方式是按字母顺序排列

给定:

>>> c = ['c', 'b', 'd', 'a', 'Z', 0, 4, 2, 1, 3]

默认排序为:
>>> sorted(c)
[0, 1, 2, 3, 4, 'Z', 'a', 'b', 'c', 'd']

它在Python3上也根本不起作用:

Python 3.4.3 (default, Feb 25 2015, 21:28:45) 
[GCC 4.2.1 Compatible Apple LLVM 6.0 (clang-600.0.56)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> c = ['c', 'b', 'd', 'a', 'Z', 0, 4, 2, 1, 3]
>>> sorted(c)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unorderable types: int() < str()

解决方案是创建一个元组,其中第一个元素是索引整数(基于项目类型),下一个元素是项目本身。Python 2和3将对第二个元素具有异构类型的元组进行排序。
给定:
>>> c = ['c', 'b', 'd', 'a', 'Z', 'abc', 0, 4, 2, 1, 3,33, 33.333]

注意字符、整数、字符串、浮点数的混合。
def f(e):
    d={int:1, float:1, str:0}
    return d.get(type(e), 0), e

>>> sorted(c, key=f)   
['Z', 'a', 'abc', 'b', 'c', 'd', 0, 1, 2, 3, 4, 33, 33.333]

或者,如果你想要一个lambda函数:

>>> sorted(c,key = lambda e: ({int:1, float:1, str:0}.get(type(e), 0), e)))  
['Z', 'a', 'abc', 'b', 'c', 'd', 0, 1, 2, 3, 4, 33, 33.333]

根据“the wolf”的评论,您还可以进行以下操作:
>>> sorted(c,key = lambda e: (isinstance(e, (float, int)), e))
['Z', 'a', 'abc', 'b', 'c', 'd', 0, 1, 2, 3, 4, 33, 33.333]

我必须同意这更好...


2
这不符合Python的多态性原则:你应该允许任何像int一样的对象被视为int来处理,而不是通过使用“type”强制要求它成为一个int -- 而是应该使用“isinstance”来允许派生类被一致地处理。 - the wolf

1
[sorted([letter for letter in c if isinstance(letter, str)]) + \
 sorted([number for number in c if isinstance(number, int)]]

应该做到。

是的,现在还很早,我还没喝咖啡,所以我假设数字也在引号里面:| 但现在已经修复了。 - a p

1
假设我们有这样一个混合列表:
c = ['s', 'a',2 , 'j', 9, 'e', 11, 't', 'k', 12, 'q']

首先,我们需要将列表切成两个部分(字符串和整数),分别进行排序,然后在最后将它们合并。以下是一种方法:

>>> c = sorted([i for i in c if not str(i).isdigit()]) + sorted([i for i in c if str(i).isdigit()])

Now you get:

>>> c
['a', 'e', 'j', 'k', 'q', 's', 't', 2, 9, 11, 12]

1
如果您有一个混合了ASCII和数字类型的列表,您需要确定什么是数字对象类型。您可以使用Numbers抽象基类来确定哪些是数字(int、float、long、complex)类的实例,包括所有派生类(bool、Decimal、Franctions等):
>>> from numbers import Number
>>> [isinstance(n, Number) for n in (0,1.1,0j,True,'a')]
[True, True, True, True, False]

一旦您知道什么是数字的实例,您可以使用Python bool创建一个主排序键,并将列表项本身作为辅助键(即元组包含[(True, 1.1), (False, 'abc'), etc])。False将低于True,就像在典型的升序排序中0<1一样,这正是我们想要的。
将其应用于您的列表(扩展版)作为示例:
>>> c = ['c', 'b', 'd', 'a', 4, 2, 1, 35, 1.1, 6L, 'aac', True]
>>> sorted(c, key=lambda e: (isinstance(e, Number), e))
['a', 'aac', 'b', 'c', 'd', 1, True, 1.1, 2, 4, 6L, 35]

请注意,不同的数字类型被正确排序(1<=True<1.1<6L<35)。

0

您还可以使用cmp参数:

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

def compare_function(a, b):
    if isinstance(a, str) and isinstance(b, int):
        return -1
    if isinstance(a, int) and isinstance(b, str):
        return 1
    return cmp(a, b)

c.sort(cmp=compare_function)

1
嗯... cmp 函数有些过时了。不过你可以通过 functools.cmp_to_key 把一个 cmp 函数转化为键函数。 - Jason Hu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接