开发者

Changing a comparison function to use keys for sorting

I have the following code written in Python 2.6, with a Task class that has an optional 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.d开发者_StackOverflow社区ue_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

The reason why I extracted the comparison function outside the class is that I wanted to be able to test it without needing to build Task instances. I use compare_by_due_date in the following code to order tasks by increasing due date, with tasks having no due date at the end of the list:

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

I understood from this answer on Code Review that I should be able to use keys instead of comparison function? Can you show me how?


You can't compare objects of all different types (such as datetime.date to None) even though 2.x does allow more comparisons between different types than 3.x, so the direct comparison of due_dates won't work.

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

tasks.sort(key=due_date_key)

This works by chaining the comparison through a tuple, so later items are only considered if earlier items are equal.


It looks like due_date_cmp sorts a list composed of datetime.date and None objects, placing the Nones at the end of the list.

You could do the same using a key which converts None objects to the maximum possible datetime.date object:

old way (using 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]

new way (using key):

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]

Thus, you might use due_date_key in your code like this:

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'))
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜