Найти объект в списке, который имеет атрибут, равный некоторому значению (которое соответствует любому условию)
У меня есть список объектов. Я хочу найти один (первый или любой) объект в этом списке, который имеет атрибут (или результат метода - независимо), равный value
.
Каков наилучший способ его найти?
Здесь тестовый пример:
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()
не будет иметь никакого значения, потому что он все равно будет итерировать через список.
ps.: Уравнение к value
является лишь примером. Конечно, мы хотим получить элемент, который удовлетворяет любому условию.
Ответы
Ответ 1
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
к x
, если вы не break
вышли из цикла.
Ответ 2
Так как это не было упомянуто только для завершения.
Хороший старый фильтр для фильтрации фильтруемых элементов.
Функциональное программирование
####### 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 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
Ответ 3
Я столкнулся с аналогичной проблемой и разработал небольшую оптимизацию для случая, когда ни один объект в списке не отвечает требованиям (для моего использования это привело к значительному улучшению производительности):
Наряду со списком test_list, я сохраняю дополнительный набор test_value_set, который состоит из значений списка, который должен быть включен. Итак, другая часть решения agf становится очень быстрой.
Ответ 4
Вы также можете реализовать расширенное сравнение с помощью метода __eq__
для своего класса Test
и использовать оператор 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"
Ответ 5
Для приведенного ниже кода xGen - это выражение автономного генератора, yFilt - объект фильтра. Обратите внимание, что для xGen возвращается дополнительный параметр None, а не выбрасывает StopIteration, когда список исчерпан.
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
Ответ 6
Вы могли бы сделать что-то вроде этого
dict = [{
"id": 1,
"name": "Doom Hammer"
},
{
"id": 2,
"name": "Rings ov Saturn"
}
]
for x in dict:
if x["id"] == 2:
print(x["name"])
Вот что я использую, чтобы найти объекты в длинном массиве объектов.