Альтернатива пониманию списка, если будет только один результат - PullRequest
61 голосов
/ 10 августа 2011

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

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

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

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

Ответы [ 4 ]

93 голосов
/ 10 августа 2011

Вы можете использовать выражение генератора и 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)
18 голосов
/ 10 августа 2011

Если вы хотите взять первый матч из потенциально большого количества, 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]

PS это такжеМожно использовать выражение генератора справа:

[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]
2 голосов
/ 10 августа 2011

Этот пост имеет пользовательскую функцию find(), которая работает довольно хорошо, и комментатор там также связан с этим методом на основе генераторов . По сути, это звучит так, как будто нет единственного отличного способа сделать это, но эти решения неплохие.

1 голос
/ 10 августа 2011

Лично я бы к этому в правильной петле.

actor = None
for actor in self.actors:
    if actor.name == actorName:
        break

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

...