复杂的排序,可以通过cmp函数轻松完成,但是如何计划Python 3?

我想对从数据库查询返回的列进行排序.
在结果中,我想按以下顺序排序:

>关键字段,按查询结果中的位置排序(因为这通常反映了后端的唯一索引).
>其余键按字母顺序排列,因为该位置反映了表格的物理字段顺序,这无关紧要.

注意:这不是我想要在数据库级别执行的操作,这是一个Python排序问题.

我可以在Python 2.7中执行以下操作(请参见下面的代码),但是想为Python 3做准备.

过去,我已经编写了基于新型operator.attrgetter / itemgetter的排序,包括连续遍历,在此过程中,您首先按一个键函数进行排序,然后再按另一个.但是我看不到3的键功能系统将如何处理分支.

#test data, mangled on purpose
data = [
    dict(fieldname="anotherkey2", pos=1, key=True),
    dict(fieldname="somekey1", pos=0, key=True),
    dict(fieldname="bfield3", pos=2, key=False),
    dict(fieldname="afield", pos=3, key=False),
    dict(fieldname="cfield", pos=4, key=False),
]

#exp keys, first, by position, then non-keys, alphabetic order
exp = ["somekey1","anotherkey2","afield","bfield3","cfield"]

def cmp2(field1, field2):

    key1, key2 = field1.get("key"), field2.get("key")

    #if both are keys, go by position in cursor results
    if key1 and key2:
        return cmp(field1["pos"], field2["pos"])

    #if neither are keys, order alphabetically
    if not (key1 or key2):
        return cmp(field1["fieldname"], field2["fieldname"])

    #otherwise, keys go first
    return cmp(key2, key1)

for func in [cmp2]:
    test_data = data[:]
    test_data.sort(cmp=func)
    got = [field["fieldname"] for field in test_data]
    try:
        msg = "fail with function:%s exp:%s:<>:%s:got" % (func.__name__, exp, got)
        assert exp == got, msg
        print ("success with %s: %s" % (func.__name__, got))
    except AssertionError,e:
        print(e)

输出:

success with cmp2: ['somekey1', 'anotherkey2', 'afield', 'bfield3', 'cfield']

此外,Sorting HOWTO中的cmp_to_key配方看起来很吓人,而且没有Python风格,每个魔术函数都有很多重复的代码.我不确定functools.cmp_to_key的相关性.

我想我能做的是用额外的属性预定义字段字典,该属性定义了排序方式.类似于sortby =(不是key,如果为key,则为pos,否则为0,字段名)元组,但是希望有一种更简洁的方法.

这行得通,但是….更好吗?

def pre_compute(data):
    for row in data:
        key, pos, fieldname = row["key"], row["pos"], row["fieldname"]
        sortby = (not key, (pos if key else 0), fieldname)
        row["sortby"] = sortby

for func in [pre_compute]:
    test_data = data[:]

    func(test_data)

    test_data.sort(key=itemgetter('sortby'))

    got = [field["fieldname"] for field in test_data]
    try:
        msg = "fail with function:%s exp:%s:<>:%s:got" % (func.__name__, exp, got)
        assert exp == got, msg
        print ("success with %s: %s" % (func.__name__, got))
    except AssertionError,e:
        print(e)

解决方法:

cmp_to_key()(独立版本或内置在functools模块中的版本)将可以与sort的cmp =参数一起使用的任意函数转换为与较新的key =参数一起使用的函数.这将是解决问题的最直接的解决方案(尽管某些评论者指出,让数据库为您完成可能会更好).

上一篇:Python-根据列的最大值删除重复项


下一篇:如何在Firebase Android的recyclerview / cardlayout中的顶部而不是底部显示最新帖子?