Pythonic比较函数

时间:2009-08-06 15:39:07

标签: comparison metaprogramming python

为了简单起见,假设我在Python中有一个Person类。该类包含firstname,lastname和dob的字段。

class Person:
  def __init__(self, firstname, lastname, dob):
    self.firstname = firstname;
    self.lastname = lastname;
    self.dob = dob;

在某些情况下,我想按姓氏排序人员列表,然后是firstname,后跟dob。在其他情况下,我想先用dob排序,然后用lastname排序,最后用firstname排序。有时我只想按名字排序。

创建第一个比较函数的天真解决方案是这样的:

def comparepeople(person1, person2):
  if cmp(person1.lastname, person2.lastname) == 0:
    if cmp(person1.firstname, person2.firstname) == 0:
      return cmp(person1.dob, person2.dob);
    return cmp(person1.firstname, person2.firstname);
  return cmp(person1.lastname, person2.lastname);

似乎应该有一种简单的方法来定义这样的比较函数,使用元编程方法,我需要做的就是按优先顺序提供字段名称,而不是编写这些非常详细,丑陋的比较方法。但我最近才开始玩Python,并没有找到类似于我所描述的内容。

所以问题是,为具有多个可比构成成员的类编写比较函数的最Pythonic方法是什么?

3 个答案:

答案 0 :(得分:10)

如果您真的想要比较功能,可以使用

def comparepeople(p1, p2):
    o1 = p1.lastname, p1.firstname, p1.dob
    o2 = p2.lastname, p2.firstname, p2.dob
    return cmp(o1,o2)

这依赖于元组比较。如果要对列表进行排序,则不应编写比较函数,而应使用关键函数:

l.sort(key=lambda p:(p.lastname, p.firstname, p.dob))

这样做的优点是a)更短,b)更快,因为每个键只计算一次(而不是在排序过程中在比较函数中创建的吨元组)。

答案 1 :(得分:4)

这是一种方式(可能不是最快):

def compare_people_flexibly(p1, p2, attrs):
    """Compare `p1` and `p2` based on the attributes in `attrs`."""
    v1 = [getattr(p1, a) for a in attrs]
    v2 = [getattr(p2, a) for a in attrs]
    return cmp(v1, v2)

def compare_people_firstname(p1, p2):
    return compare_people_flexibly(p1, p2, ['firstname', 'lastname', 'dob'])

def compare_people_lastname(p1, p2):
    return compare_people_flexibly(p1, p2, ['lastname', 'firstname', 'dob'])

这是有效的,因为getattr可用于获取由字符串命名的属性,并且因为Python会根据您对第一个不相等项的比较来比较列表。

另一种方式:

def compare_people_flexibly(p1, p2, attrs):
    """Compare `p1` and `p2` based on the attributes in `attrs`."""
    for a in attrs:
        c = cmp(getattr(p1, a), getattr(p2, a))
        if c:
            return c
    return 0

这样做的好处是它不会构建两个完整的属性列表,因此如果属性列表很长,或者在第一个属性上完成了许多比较,则可能会更快。

最后,正如马丁所提到的,你可能需要一个关键功能而不是比较功能:

def flexible_person_key(attrs):
    def key(p):
        return [getattr(p, a) for a in attrs]
    return key

l.sort(key=flexible_person_key('firstname', 'lastname', 'dob'))

答案 2 :(得分:0)

你能不能在课堂上使用比较方法,参见__cmp__和其他丰富的比较方法......