Понимание списка и len () против простого цикла for - PullRequest
5 голосов
/ 03 ноября 2010

Я должен взять список слов и сосчитать все слова в нем, которые имеют длину 2 или более символов и где первый и последний символы равны.

Я нашел два возможных решения:

result = 0
for word in words:
    if len(word) >= 2 and word[0] == word[-1]:
        result += 1
return result

против

return len([word for word in words if len(word) >= 2 and word[0] == word[-1]])

Какой из них будет предпочтительным решением?Или есть еще лучшие?

Ответы [ 5 ]

14 голосов
/ 03 ноября 2010

Во втором примере генераторное выражение будет лучше, чем list-comp, если ваш список большой.

sum(1 for word in words if len(word) >= 2 and word[0] == word[-1])
4 голосов
/ 03 ноября 2010

Первое, безусловно, будет предпочтительным решением в Python.

Не забудьте свой Zen of Python:

Дзен Python, Тим Питерс

Красиво лучше, чем безобразно.

Явное лучше, чем неявное.

Простое лучше, чем сложное.

Сложный лучше, чем сложный.

Квартира лучше, чем вложенная.

Разреженный лучше, чем плотный.

Читаемость имеет значение.

Особых случаев недостаточно, чтобы нарушать правила.

Хотя практичность превосходит чистоту.

Ошибки никогда не должны проходить бесшумно.

Если явно не молчать.

Перед лицом двусмысленности откажитесь от искушения угадать.

Должен быть один - и желательно только один - очевидный способ сделать это.

Хотя этот путь поначалу может быть неочевиден, если вы не голландец

Теперь лучше, чем никогда.

Хотя никогда не бывает лучше, чем прямо сейчас.

Если реализацию сложно объяснить, это плохая идея.

Если реализацию легко объяснить, это может быть хорошей идеей.

Пространства имен - одна из замечательных идей - давайте сделаем еще больше!

Кроме того, ваши решения хороши.

2 голосов
/ 03 ноября 2010

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

Любая версия может быть дополнительно сокращена / улучшена

result = 0
for word in words:
    result += int(len(word) >= 2 and word[0] == word[-1])
return result

Преобразования int (), строго говоря, не нужны, поскольку True является своего рода 1, но может быть лучше для удобства чтения.Тот же подход может применяться к пониманию:

return sum(len(word) >= 2 and word[0] == word[-1] for word in words)

Если вы хотите использовать len (), я бы указал читателю на тот факт, что значения на самом деле не имеют значения:

len(1 for word in words if len(word) >= 2 and word[0] == word[-1])
1 голос
/ 03 ноября 2010

Некоторые другие варианты, которые вы можете рассмотреть:

Во-первых, вы можете разбить условие фильтра на функцию. В любом случае это условие хорошо, но если оно станет более сложным, я определенно сделаю следующее:

def check(word):
    return len(word) >= 2 and word[0] == word[-1]
sum(1 for word in words if check(word))

Далее, если создание списка (как в исходном понимании списка) приемлемо, то вы можете сделать это:

len(filter(check, words))

Есть файл itertools.ifilter, но если вы используете его, вам нужно будет снова использовать выражение sum, чтобы оно не получилось более ясным.

Трюк sum появляется так часто, что я удивляюсь, что нет стандартного библиотечного вызова для подсчета количества элементов в итераторе (если он есть, я его не нашел). Кроме того, было бы целесообразно, если бы len потреблял и подсчитывал количество записей в итераторе, если у него нет __len__, но его нет.

1 голос
/ 03 ноября 2010

Оба довольно хороши.

Есть небольшие различия:

Понимание списка возвращает другой список, который вы передаете len .Первое решение позволяет избежать создания другого списка.

...