【问题标题】:Complex sorting, easily done with cmp function, but how do I plan for Python 3?复杂的排序,用 cmp 函数轻松完成,但我如何为 Python 3 做计划?
【发布时间】:2017-07-12 02:12:29
【问题描述】:

我想对从数据库查询返回的列进行排序以进行展示。 在结果中,我想按以下方式排序:

  1. 首先是键字段,按查询结果中的位置排序(因为这通常反映后端的唯一索引)。

  2. 其余键按字母顺序排列,因为位置反映了不感兴趣的表物理字段顺序。

注意:这不是我想在数据库级别做的事情,这是一个 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 = (not key, pos if key else 0, fieldname) 元组的东西,但希望有更简洁的方法。

这行得通,但是....还有什么更好的吗?

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)

【问题讨论】:

  • 这不能在数据库级别完成吗?即查询本身中的orderby。
  • 您编写的比较器可以使用元组键和条件表达式转换为键函数,但让数据库处理排序可能是更好的方法。
  • @DeepSpace。不,真的不能。这相当于生成或硬编码一个查询,其中的列为select somekey1, anotherkey2, afield, bfield3, cfield,实际上超出了范围。这不是结果集的order by,而是要显示的列的顺序。此外,数据库方面仅供参考,核心问题是如何在 Python 3 中进行这种排序。

标签: python python-2.7 python-3.x sorting


【解决方案1】:

cmp_to_key()(独立版本或内置于 functools 模块的版本)将可与 sort 的 cmp= 参数一起使用的任意函数转换为可与较新的 key= 参数一起使用的函数。这将是解决您的问题的最直接的解决方案(尽管正如一些评论者所指出的那样,让数据库为您做这件事可能会更好)。

【讨论】:

  • test_data.sort(key=functools.cmp_to_key(cmp2)) 有效 - 其中cmp2 是我的原始功能,所以我会接受这个。哇,比预期的要容易得多(而且很神奇!)。
【解决方案2】:

按字段是否为关键字段排序,然后是位置或字段名称,取决于是否为关键字段。

def keyfunc(field):
    return (not field['key'], field['pos'] if field['key'] else field['fieldname'])

【讨论】:

  • 是的,这也有效。有点像我的 sortby 元组,但不需要向数据中注入垃圾。我认为这可能是更清洁的方法,但 cmp_to_key 在更复杂的情况下可能是必要的。
猜你喜欢
  • 2022-08-11
  • 2013-10-17
  • 2015-06-22
  • 1970-01-01
  • 1970-01-01
  • 2011-10-30
  • 2015-06-10
  • 2023-03-27
  • 1970-01-01
相关资源
最近更新 更多