2009-08-06 9 views
1

簡潔にするために、私はPythonでPersonクラスを持っているとしましょう。このクラスには、firstname、lastname、およびdobの各フィールドがあります。Pythonicの比較関数

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

ある種の状況では、姓でPersonのリストをソートし、続いてfirstnameの後にdobを並べ替えたい場合もあります。他の状況では、まずdobで、次にlastnameで、最後にfirstnameでソートする必要があります。そして、時には私は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で遊んで始めて、私が何を記述しているのか分かりませんでした。

だから質問には、比較可能な構成メンバーが複数あるクラスの比較関数を書くのに最もPythonの方法はありますか?ここで

+4

を比較メソッドを使用することはできません - それらのセミコロンを削除します!彼らは有用な目的を果たさず、良いPythonスタイルではありません。 –

答えて

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回だけ計算されます(並べ替え中に比較関数で作成されるタプルの数量ではありません)。

+1

RobertoはNedの答えについてコメントしていたので、 'l.sort(operator.attrgetter( 'lastname'、 'firstname'、 'dob'))'は短くはありません。 –

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

Pythonはリストを比較するので、あなたが期待するようGETATTRがに基づいて、文字列で指定された属性を取得するために使用することができ、これは動作します最初の非等しい項目の比較。

別の方法:

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 

これは、属性の2つの完全なリストを作成しませんので、属性リストが長い、または最初の属性に多くの比較が完了している場合であればより速くすることができるという利点を有します。

最後に、マーティンは言及として、あなたはむしろ比較関数よりもキーの機能が必要になる場合があります

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

flexible_person_key = operator.attrgetter;) –

+0

しかし、Python 2.5のattrgetterは、複数の引数を持つタプルを得るために複数の引数で呼び出すことができるので、 –

+0

私はそれを知らなかった!レッスンをありがとう。 –

0

あなたは余談として... __cmp__およびその他のリッチ比較方法を参照して、クラスに

+0

要は、異なる状況で異なるように比較関数が必要であり、たとえPersonに__cmp__を実装したとしても、Nedの実装のようなものを使用しなければならないでしょう。 –