Альтернатива пониманию списка, если будет только один результат
Я начинаю привыкать, чтобы перечислить понимание в Python, но, боюсь, я использую его несколько ненадлежащим образом. Я несколько раз сталкивался с сценарием, когда я использую понимание списков, но сразу беру первый (и единственный) элемент из создаваемого списка. Вот пример:
actor = [actor for actor in self.actors if actor.name==actorName][0]
(self.actors содержит список объектов, и я пытаюсь перейти к одному с определенным (строковым) именем, которое находится в имени actorName.)
Я пытаюсь вытащить объект из списка, который соответствует параметру, который я ищу. Является ли этот метод необоснованным? Висячие [0] заставляют меня чувствовать себя немного небезопасно.
Ответы
Ответ 1
Вместо этого вы можете использовать выражение генератора и next
. Это было бы более эффективным, так как промежуточный список не создается, и итерация может останавливаться после обнаружения совпадения:
actor = next(actor for actor in self.actors if actor.name==actorName)
И как указывает senderle, еще одно преимущество этого подхода состоит в том, что вы можете указать значение по умолчанию, если совпадение не найдено:
actor = next((actor for actor in self.actors if actor.name==actorName), None)
Ответ 2
Если вы хотите взять первый матч из потенциально большого количества, next(...)
отлично подходит.
Но если вы ожидаете ровно одного, подумайте об этом:
[actor] = [actor for actor in self.actors if actor.name==actorName]
Это всегда сканируется до конца, но в отличие от [0]
, деструктуризация в [actor]
выдает ошибку ValueError, если найдено 0 или более одного совпадения.
Возможно, это даже важнее, чем обнаружение ошибок. Это сообщает читателю ваше предположение.
Если вы хотите использовать значение по умолчанию для 0 совпадений, но все еще ловите> 1 совпадения:
[actor] = [actor for actor in self.actors if actor.name==actorName] or [default]
P.S. также возможно использовать выражение генератора справа:
[actor] = (actor for actor in self.actors if actor.name==actorName)
который должен быть чуть более эффективным. Вы также можете использовать синтаксис кортежа с левой стороны - выглядит более симметрично, но запятая уродлива и ее слишком легко пропустить ИМХО:
(actor,) = (actor for actor in self.actors if actor.name==actorName)
actor, = (actor for actor in self.actors if actor.name==actorName)
(в любом случае, список против синтаксиса кортежей на левой стороне чисто косметический, не влияет на поведение)
Ответ 3
Этот пост имеет пользовательскую функцию find()
, которая работает достаточно хорошо, а комментатор также связан с этот метод основан на генераторах. В принципе, похоже, что нет единственного замечательного способа сделать это, но эти решения неплохие.
Ответ 4
Лично я бы сделал это в правильном цикле.
actor = None
for actor in self.actors:
if actor.name == actorName:
break
Это довольно долго, но у него есть то преимущество, что он прекращает цикл, как только будет найдено совпадение.