web-dev-qa-db-ja.com

何らかの値に等しい(任意の条件を満たす)属性を持つリスト内のオブジェクトを検索します

オブジェクトのリストがあります。このリストで、valueに等しい属性(またはメソッドの結果-何でも)を持つ1つの(最初のまたは何でも)オブジェクトを見つけたいです。

それを見つける最良の方法は何ですか?

テストケースは次のとおりです。

  class Test:
      def __init__(self, value):
          self.value = value

  import random

  value = 5

  test_list = [Test(random.randint(0,100)) for x in range(1000)]

  # that I would do in Pascal, I don't believe isn't anywhere near 'Pythonic'
  for x in test_list:
      if x.value == value:
          print "i found it!"
          break

ジェネレーターとreduce()を使用しても、リストを繰り返し処理するため、違いは生じないと思います。

追伸:valueの方程式は単なる例です。もちろん、任意の条件を満たす要素を取得したいです。

163
seler
next((x for x in test_list if x.value == value), None)

これは、条件に一致するリストから最初のアイテムを取得し、一致するアイテムがない場合はNoneを返します。それは私の好みの単一表現形式です。

しかしながら、

for x in test_list:
    if x.value == value:
        print "i found it!"
        break

素朴なループブレイクバージョンは完全にPythonicです-簡潔で、明確で、効率的です。ワンライナーの動作と一致させるには:

for x in test_list:
    if x.value == value:
        print "i found it!"
        break
else:
    x = None

これにより、Noneをループ外にしない場合、xbreakに割り当てられます。

328
agf

完了のためだけに言及されていないので。フィルタリングする要素をフィルタリングするための優れたol 'フィルター.

関数型プログラミングftw。

####### Set Up #######
class X:

    def __init__(self, val):
        self.val = val

elem = 5

my_unfiltered_list = [X(1), X(2), X(3), X(4), X(5), X(5), X(6)]

####### Set Up #######

### Filter one liner ### filter(lambda x: condition(x), some_list)
my_filter_iter = filter(lambda x: x.val == elem, my_unfiltered_list)
### Returns a flippin' iterator at least in Python 3.5 and that's what I'm on

print(next(my_filter_iter).val)
print(next(my_filter_iter).val)
print(next(my_filter_iter).val)

### [1, 2, 3, 4, 5, 5, 6] Will Return: ###
# 5
# 5
# Traceback (most recent call last):
#   File "C:\Users\mousavin\workspace\Scripts\test.py", line 22, in <module>
#     print(next(my_filter_iter).value)
# StopIteration


# You can do that None stuff or whatever at this point, if you don't like exceptions.

一般的に、pythonリストの内包表記が優先されること、または少なくともそれが私が読んだものであることは知っていますが、この問題が正直であるとは思いません。もちろんPythonはFP言語ではありませんが、Map/Reduce/Filterは完全に読みやすく、関数型プログラミングの標準的なユースケースの最も標準的なものです。

それであなたは行き​​ます。あなたの関数型プログラミングを知ってください。

フィルター条件リスト

これより簡単になることはありません。

next(filter(lambda x: x.val == value,  my_unfiltered_list)) # Optionally: next(..., None) or some other default value to prevent Exceptions
15
Nima Mousavi

私はちょうど同様の問題に遭遇し、リスト内のオブジェクトが要件を満たしていない場合の小さな最適化を考案しました(私のユースケースでは、これにより大きなパフォーマンスの改善がもたらされました):

リストtest_listとともに、追加のセットtest_value_setを保持します。このセットは、フィルタリングする必要があるリストの値で構成されています。そのため、agfのソリューションのelse部分は非常に高速になります。

1
user1578297

Testクラスに __eq__ メソッドを使用してリッチ比較を実装し、in演算子を使用することもできます。これが最善のスタンドアロンの方法であるかどうかはわかりませんが、Testに基づいてvalueインスタンスを他のどこかで比較する必要がある場合、これは便利です。

class Test:
    def __init__(self, value):
        self.value = value

    def __eq__(self, other):
        """To implement 'in' operator"""
        # Comparing with int (assuming "value" is int)
        if isinstance(other, int):
            return self.value == other
        # Comparing with another Test object
        Elif isinstance(other, Test):
            return self.value == other.value

import random

value = 5

test_list = [Test(random.randint(0,100)) for x in range(1000)]

if value in test_list:
    print "i found it"
0
tm-

以下のコードでは、xGenは匿名ジェネレーター式であり、yFiltはフィルターオブジェクトです。 xGenの場合、リストを使い果たしたときにStopIterationをスローするのではなく、追加のNoneパラメーターが返されることに注意してください。

arr =((10,0), (11,1), (12,2), (13,2), (14,3))

value = 2
xGen = (x for x in arr if x[1] == value)
yFilt = filter(lambda x: x[1] == value, arr)
print(type(xGen))
print(type(yFilt))

for i in range(1,4):
    print('xGen: pass=',i,' result=',next(xGen,None))
    print('yFilt: pass=',i,' result=',next(yFilt))

出力:

<class 'generator'>
<class 'filter'>
xGen: pass= 1  result= (12, 2)
yFilt: pass= 1  result= (12, 2)
xGen: pass= 2  result= (13, 2)
yFilt: pass= 2  result= (13, 2)
xGen: pass= 3  result= None
Traceback (most recent call last):
  File "test.py", line 12, in <module>
    print('yFilt: pass=',i,' result=',next(yFilt))
StopIteration
0
edW