インスタンスのリストが 2 つあります。
list1
list2
各インスタンスには、ID、名前などの変数が含まれています...
list2 を反復処理していますが、list1 に存在しないエントリを見つけたいと考えています。
例えば..
for entry in list2:
if entry.id in list1:
<do something>
double for ループなしでこれを行う方法を見つけたいと思っています。簡単な方法はありますか?
私は次のようなことをするかもしれません:
set1 = set((x.id,x.name,...) for x in list1)
difference = [ x for x in list2 if (x.id,x.name,...) not in set1 ]
...
インスタンスの追加の (ハッシュ可能な) 属性はどこにありますか? 一意にするために十分な属性を含める必要があります。
これは、O(N*M) アルゴリズムを受け取り、それを O(max(N,M)) アルゴリズムに変換します。
ちょっとした考え...
class Foo(object):
def __init__(self, id, name):
self.id = id
self.name = name
def __repr__(self):
return '({},{})'.format(self.id, self.name)
list1 = [Foo(1,'a'),Foo(1,'b'),Foo(2,'b'),Foo(3,'c'),]
list2 = [Foo(1,'a'),Foo(2,'c'),Foo(2,'b'),Foo(4,'c'),]
したがって、通常、これは機能しません。
print(set(list1)-set(list2))
# set([(1,b), (2,b), (3,c), (1,a)])
Foo
しかし、2 つのインスタンスが等しいとはどういう意味かを教えることができます。
def __hash__(self):
return hash((self.id, self.name))
def __eq__(self, other):
try:
return (self.id, self.name) == (other.id, other.name)
except AttributeError:
return NotImplemented
Foo.__hash__ = __hash__
Foo.__eq__ = __eq__
そしていま:
print(set(list1)-set(list2))
# set([(3,c), (1,b)])
もちろん、後でモンキー パッチを適用するのではなく、クラス定義時に__hash__
and __eq__
onを定義できる可能性が高くなります。Foo
class Foo(object):
def __init__(self, id, name):
self.id = id
self.name = name
def __repr__(self):
return '({},{})'.format(self.id, self.name)
def __hash__(self):
return hash((self.id, self.name))
def __eq__(self, other):
try:
return (self.id, self.name) == (other.id, other.name)
except AttributeError:
return NotImplemented
そして、私の好奇心を満たすために、ここにベンチマークがあります:
In [34]: list1 = [Foo(1,'a'),Foo(1,'b'),Foo(2,'b'),Foo(3,'c')]*10000
In [35]: list2 = [Foo(1,'a'),Foo(2,'c'),Foo(2,'b'),Foo(4,'c')]*10000
In [40]: %timeit set1 = set((x.id,x.name) for x in list1); [x for x in list2 if (x.id,x.name) not in set1 ]
100 loops, best of 3: 15.3 ms per loop
In [41]: %timeit set1 = set(list1); [x for x in list2 if x not in set1]
10 loops, best of 3: 33.2 ms per loop
そのため、@mgilson の方法の方が高速ですが、__hash__
and__eq__
を定義するとFoo
コードが読みやすくなります。
使用できますfilter
difference = filter(lambda x: x not in list1, list2)
Python 2 では、必要なリストが返されます。Python 3 ではfilter
、リストに変換したいオブジェクトを返します。
もしかしてこういうこと?
In [1]: list1 = [1,2,3,4,5]
In [2]: list2 = [4,5,6,7]
In [3]: final_list = [x for x in list1 if x not in list2]