更改比较功能以使用键进行排序

时间:2011-02-02 20:00:21

标签: python sorting

我有以下用Python 2.6编写的代码,Task类有一个可选的due_date

class Task(models.Model):
    due_date = models.DateField(null = True, blank = True)

    @staticmethod
    def compare_by_due_date(t1, t2):
        return due_date_cmp(t1.due_date, t2.due_date)

def due_date_cmp(t1, t2):
    if t1 is None and t2 is None:
        return 0;
    if t1 is None:
        return 1
    if t2 is None:
        return -1
    return (t1 - t2).days

我在类之外提取比较函数的原因是我希望能够在不需要构建Task实例的情况下对其进行测试。我在以下代码中使用compare_by_due_date来通过增加截止日期来排序任务,任务在列表末尾没有截止日期:

tasks = Task.objects.all()
tasks.sort(Task.compare_by_due_date)

我从this answer on Code Review了解到我应该可以使用keys instead of comparison function?你能告诉我怎么样吗?

2 个答案:

答案 0 :(得分:4)

您无法比较所有不同类型的对象(例如datetime.date到None),即使2.x确实允许在3.x之外的不同类型之间进行更多比较,因此due_dates的直接比较将不起作用。

def due_date_key(t):
  return (t.due_date is None, t.due_date)

tasks.sort(key=due_date_key)

这可以通过元组链接比较来工作,因此只有在前面的项目相同时才考虑后面的项目。

答案 1 :(得分:2)

看起来due_date_cmp对包含datetime.dateNone个对象的列表进行排序,将None放在列表的末尾。

您可以使用将None个对象转换为最大可能datetime.date对象的键来执行相同操作:

旧方法(使用cmp):

import datetime as dt

def due_date_cmp(t1, t2):
    if t1 == None and t2 == None:
        return 0;
    if t1 == None:
        return 1
    if t2 == None:
        return -1
    return (t1 - t2).days


dates=[dt.date(2000,1,1),None,dt.date(1999,1,1),None,dt.date(2002,1,1)]
dates.sort(cmp=due_date_cmp)
print(dates)
# [datetime.date(1999, 1, 1), datetime.date(2000, 1, 1), datetime.date(2002, 1, 1), None, None]

新方式(使用密钥):

def due_date_key(t):
    if t is None:
        return dt.date(dt.MAXYEAR,12,31)
    else:
        return t

dates=[dt.date(2000,1,1),None,dt.date(1999,1,1),None,dt.date(2002,1,1)]
dates.sort(key=due_date_key)
print(dates)

# [datetime.date(1999, 1, 1), datetime.date(2000, 1, 1), datetime.date(2002, 1, 1), None, None]

因此,您可以在代码中使用due_date_key,如下所示:

import operator
class Task(models.Model):
    @property
    def due_date_key(self):
        due_date=self.due_date
        if due_date is None:
            return dt.date(dt.MAXYEAR,12,31)
        else:
            return due_date

tasks = Task.objects.all()
tasks.sort(key=operator.attrgetter('due_date_key'))