Подтвердить что ты не робот

Альтернатива пониманию списка, если будет только один результат

Я начинаю привыкать, чтобы перечислить понимание в Python, но, боюсь, я использую его несколько ненадлежащим образом. Я несколько раз сталкивался с сценарием, когда я использую понимание списков, но сразу беру первый (и единственный) элемент из создаваемого списка. Вот пример:

actor = [actor for actor in self.actors if actor.name==actorName][0]

(self.actors содержит список объектов, и я пытаюсь перейти к одному с определенным (строковым) именем, которое находится в имени actorName.)

Я пытаюсь вытащить объект из списка, который соответствует параметру, который я ищу. Является ли этот метод необоснованным? Висячие [0] заставляют меня чувствовать себя немного небезопасно.

4b9b3361

Ответ 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

Это довольно долго, но у него есть то преимущество, что он прекращает цикл, как только будет найдено совпадение.