В Python лучше использовать списки или циклы for-each? - PullRequest
37 голосов
/ 17 мая 2010

Что из следующего лучше использовать и почему?

Метод 1:

for k, v in os.environ.items():
       print "%s=%s" % (k, v)

Метод 2:

print "\n".join(["%s=%s" % (k, v) 
   for k,v in os.environ.items()])

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

(Код взят из Погружение в Python )

Ответы [ 7 ]

38 голосов
/ 17 мая 2010

Если итерация выполняется для ее побочного эффекта (как это происходит в вашем примере "print"), то цикл более понятен.

Если итерация выполняется для построения составного значения, то списочные значения обычно более читабельны.

26 голосов
/ 17 мая 2010

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

Во многих других случаях вам понадобится предоставить фактический список другой функции или методу, и понимание списка - самый простой и читаемый способ сделать это.

Пример, который наглядно продемонстрировал бы превосходство списка, можно было бы сделать, заменив пример print на пример, включающий создание другого фактического списка, добавив к одному на каждой итерации цикла for:

L = []
for x in range(10):
    L.append(x**2)

Дает такой же L как:

L = [x**2 for x in range(10)]
14 голосов
/ 17 мая 2010

Я считаю первый пример лучше - менее многословным, более четким и более читабельным.

На мой взгляд, в конце концов, сделайте все, что в ваших силах:

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

- из "Структуры и интерпретации компьютерных программ" Абельсона и Суссмана

Кстати, поскольку вы только начинаете изучать Python, начните изучать новый синтаксис String Formatting прямо сейчас:

for k, v in os.environ.items():
    print "{0}={1}".format(k, v)
13 голосов
/ 17 мая 2010

Понимание списка более чем в два раза быстрее, чем явный цикл. Основываясь на вариации Бена Джеймса, но заменив x ** 2 более тривиальной функцией x + 2, две альтернативы:

def foo(n):
  L = []
  for x in xrange(n):
    L.append(x+2)
  return L


def bar(n):
  return [x+2 for x in xrange(n)]

Результат синхронизации:

In [674]: timeit foo(1000)
10000 loops, best of 3: 195 us per loop

In [675]: timeit bar(1000)
10000 loops, best of 3: 81.7 us per loop

Список понимания побеждает с большим отрывом.

Я согласен, что удобочитаемость должна быть приоритетом перед оптимизацией производительности. Однако читаемость в глазах смотрящего. Когда я впервые изучаю Python, понимание списка - это странная вещь, которую мне трудно понять! : -O Но как только я привык к этому, это становится действительно хорошей записью короткой руки. Если вы хотите стать опытным в Python, вы должны освоить понимание списка.

4 голосов
/ 17 мая 2010

Первый на мой взгляд, потому что:

  • Он не строит огромную строку.
  • Он не создает огромный список (его можно легко исправить с помощью генератора, удалив []).

В обоих случаях доступ к элементам осуществляется одинаково (с помощью итератора словаря).

3 голосов
/ 17 мая 2010

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

2 голосов
/ 17 мая 2010

Я согласен с @Ben, @Tim, @Steven:

  • удобочитаемость - самая важная вещь (выполните «импортировать это», чтобы напомнить себе о том, что есть)
  • listcomp может или не может быть намного быстрее, чем версия итеративного цикла ... это зависит от общего числа выполненных вызовов функции
  • если вы делаете решаете использовать списки с большими наборами данных, лучше использовать выражения генератора вместо

Пример:

print "\n".join("%s=%s" % (k, v) for k,v in os.environ.iteritems())

в приведенном выше фрагменте кода я произвел два изменения ... Я заменил listcomp на genexp и изменил вызов метода на iteritems(). [эта тенденция движется вперед, как в Python 3, iteritems() заменяется и переименовывается в items().]

...