5

したがって、set.intersection()を使用して2つのセットの共通部分を計算する簡単な方法があります。ただし、次の問題があります。

class Person(Object):                    
    def __init__(self, name, age):                                                      
        self.name = name                                                                
        self.age = age                                                                  

l1 = [Person("Foo", 21), Person("Bar", 22)]                                             
l2 = [Person("Foo", 21), Person("Bar", 24)]                                             

union_list = list(set(l1).union(l2))                                           
# [Person("Foo", 21), Person("Bar", 22), Person("Bar", 24)]

(これObjectは、基本的な機能を実装するORMによって提供される基本クラスで__hash__あり、基本__eq__的にクラスのすべてのメンバーをハッシュに追加します。つまり、__hash__返されるのはクラスのすべての要素のハッシュになります)

.nameこの段階では、たとえば、を見つけるためだけに、集合交差演算を実行したいと思いますPerson('Bar', -1).intersection(union_list) #= [Person("Bar", -1), Person("Bar", 22), Person("Bar", 24)]。(この時点での典型的な.intersection()ものは私に何も与えません、これは元の集合和集合をオーバーライドするので、私はオーバーライドすることもクラス上で行うこともできません(__hash____eq__思うPerson

Python 2.xでこれを行うための最良の方法は何ですか?

編集:ソリューションはに依存する必要がないことsetに注意してください。ただし、ユニオンを見つけてから交差点を見つける必要があるので、これはセットに適しているように感じます(ただし、問題が解決する限り、価値があると思われる魔法を使用するソリューションを受け入れます!)

4

6 に答える 6

7

のように聞こえる

>>> class Person:
...     def __init__(self, name, age):
...         self.name = name
...         self.age = age
...     def __eq__(self, other):
...         return self.name == other.name
...     def __hash__(self):
...         return hash(self.name)
...     def __str__(self):
...         return self.name
...
>>> l1 = [Person("Foo", 21), Person("Bar", 22)]
>>> l2 = [Person("Foo", 21), Person("Bar", 24)]
>>> union_list = list(set(l1).union(l2))
>>> [str(l) for l in union_list]
['Foo', 'Bar']

nameあなたのユニークキーは何ですか?

于 2012-05-30T09:18:20.803 に答える
2

どうですか:

d1 = {p.name:p for p in l1}
d2 = {p.name:p for p in l2}

intersectnames = set(d1.keys()).intersection(d2.keys)
intersect = [d1[k] for k in intersectnames]

ORMを使用する方が速いintersectnames場合があります。その場合、辞書を作成せずに、リストに名前を収集するだけです。

于 2012-05-31T07:15:13.250 に答える
2

私は自分の質問に答えるのが嫌いなので、これを「答え」としてマークするのはまだしばらく延期します。

これを行う方法は次のとおりです。

import types
p = Person("Bar", -1)
new_hash_method = lambda obj: hash(obj.name)
p.__hash__ = types.MethodType(new_hash_method, p)
for i in xrange(0, len(union_list)):
    union_list[i].__hash__ = types.MethodType(new_hash_method, union_list[i])
set(union_list).intersection(p)

それは確かに汚れていて、依存していますtypes.MethodTypeが、実際には数千のアイテムが含まれる可能性があるため、これまでに提案された最良のソリューション(glglglのソリューション)よりも集中的ではありunion_listません。これにより、実行するたびにオブジェクトを再作成する手間が省けます。この交差点の手順。

于 2012-05-31T06:58:53.990 に答える
1

これは不格好ですが...

set(p for p in union_list for q in l2 if p.name == q.name and p.age != q.age) | (set(p for p in l2 for q in union_list if p.name == q.name and p.age != q.age))
# {person(name='Bar', age=22), person(name='Bar', age=24)}
于 2012-05-30T09:09:55.937 に答える
1

ageを比較に関して無関係にしたい場合は、をオーバーライド__hash__()して、__eq__()に入れておく必要がありPersonますObject

この(および同様の)コンテキストでのみこの動作が必要な場合は、を含むラッパーオブジェクトを作成し、次のPersonように異なる動作をすることができます。

class PersonWrapper(Object):
    def __init__(self, person):
        self.person = person
    def __eq__(self, other):
        if hasattr(other, 'person'):
            return self.person.name == other.person.name
        else:
            return self.person.name == other.name
    def __hash__(self):
        return hash(self.person.name)

そしてします

union_list = list(set(PersonWrapper(i) for i in l1).union(PersonWrapper(i) for i in l2))
# [Person("Foo", 21), Person("Bar", 22), Person("Bar", 24)]

(未テスト)

于 2012-05-30T09:50:29.653 に答える
0

__hash__このようなセットを使用する場合は、と比較メソッドをオーバーライドする必要があります。

そうでない場合は、

Person("Foo", 21) == Person("Foo", 21)

常にfalseになります。

オブジェクトがORMによって管理されている場合は、オブジェクトの比較方法を確認する必要があります。通常、オブジェクトIDのみを調べ、比較は両方のオブジェクトが管理されている場合にのみ機能します。ORMから取得したオブジェクトを、dbに永続化する前に自分で作成したインスタンスと比較しようとすると、それらは異なる可能性があります。とにかく、ORMは、独自の比較ロジックを提供することに問題はないはずです。

ただし、何らかの理由で__hash__and__eq__をオーバーライドできない場合は、元のオブジェクトとの交差および結合にセットを使用できません。あなたは出来る:

  • 交差点/和集合を自分で計算する
  • 同等のラッパークラスを作成します。

    class Person:                    
        def __init__(self, name, age):                                                      
            self.name = name                                                                
            self.age = age                                                                  
    
    l1 = [Person("Foo", 21), Person("Bar", 22)]                                             
    l2 = [Person("Foo", 21), Person("Bar", 24)]                                             
    
    class ComparablePerson:
        def __init__(self, person):
            self.person = person
    
        def __hash__(self):
            return hash(self.person.name) + 31*hash(self.person.age)
    
        def __eq__(self, other):
            return (self.person.name == other.person.name and
                    self.person.age == other.person.age)
        def __repr__(self):
            return "<%s - %d>" % (self.person.name, self.person.age)
    
    c1 = set(ComparablePerson(p) for p in l1)
    c2 = set(ComparablePerson(p) for p in l2)
    
    print c1
    print c2
    print c1.union(c2)
    print c2.intersection(c1)
    
于 2012-05-30T08:59:04.563 に答える