Обоснование Python предпочтительнее для синтаксиса
В чем смысл обоснованного использования конструкторов цикла for i in xrange(...)
-style в Python? Для простых целых циклов разница в надбавке существенна. Я провел простой тест с использованием двух частей кода:
Файл idiomatic.py
:
#!/usr/bin/env python
M = 10000
N = 10000
if __name__ == "__main__":
x, y = 0, 0
for x in xrange(N):
for y in xrange(M):
pass
Файл cstyle.py
:
#!/usr/bin/env python
M = 10000
N = 10000
if __name__ == "__main__":
x, y = 0, 0
while x < N:
while y < M:
y += 1
x += 1
Результаты профилирования были следующими:
bash-3.1$ time python cstyle.py
real 0m0.109s
user 0m0.015s
sys 0m0.000s
bash-3.1$ time python idiomatic.py
real 0m4.492s
user 0m0.000s
sys 0m0.031s
Я могу понять, почему версия Pythonic работает медленнее - я полагаю, что она имеет много общего с вызовом xrange N раз, возможно, это может быть устранено, если есть способ перемотать генератор. Однако с этой разницей во времени исполнения, почему вы предпочитаете использовать версию Pythonic?
Изменить: Я снова провел тесты, используя код, предоставленный г-ном Мартелли, и теперь результаты были действительно лучше:
Я думал, что перечислил выводы из этой темы:
1) Много кода в области модуля - плохая идея, даже если код заключен в блок if __name__ == "__main__":
.
2) * Любопытно, что изменение кода, принадлежащего thebadone
к моей неправильной версии (позволяя y расти без перезагрузки), создало небольшую разницу в производительности даже для больших значений M и N.
Ответы
Ответ 1
Здесь правильное сравнение, например. в loop.py:
M = 10000
N = 10000
def thegoodone():
for x in xrange(N):
for y in xrange(M):
pass
def thebadone():
x = 0
while x < N:
y = 0
while y < M:
y += 1
x += 1
Весь существенный код должен всегда находиться в функциях - помещение сто миллионов петель на верхнем уровне модуля показывает безрассудное игнорирование производительности и делает издевательство над любыми попытками измерения указанной производительности.
Как только вы это сделали, вы увидите:
$ python -mtimeit -s'import loop' 'loop.thegoodone()'
10 loops, best of 3: 3.45 sec per loop
$ python -mtimeit -s'import loop' 'loop.thebadone()'
10 loops, best of 3: 10.6 sec per loop
Итак, правильно измеренный, плохой способ, который вы защищаете, примерно в 3 раза медленнее, чем хороший способ, который продвигает Python. Надеюсь, это заставит вас пересмотреть свою ошибочную пропаганду.
Ответ 2
Вы забыли reset y до 0 после внутреннего цикла.
#!/usr/bin/env python
M = 10000
N = 10000
if __name__ == "__main__":
x, y = 0, 0
while x < N:
while y < M:
y += 1
x += 1
y = 0
ed: 20.63s после исправления по сравнению с 6.97 с использованием xrange
Ответ 3
полезно для итерации над структурами данных
Синтаксис for i in ...
отлично подходит для итерации над структурами данных. На языке более низкого уровня вы обычно выполняете итерацию по массиву, индексируемому с помощью int, но с синтаксисом python вы можете исключить шаг индексирования.
Ответ 4
это не прямой ответ на вопрос, но я хочу открыть диалог немного больше на xrange()
. две вещи:
а. что-то не так с одним из операторов OP, которые никто еще не исправил (да, в дополнение к ошибке в коде сброса y
):
"Я предполагаю, что он имеет много общего с вызовом xrange N раз..."
в отличие от традиционного подсчета for
циклов, Python больше похож на оболочку foreach
... зацикливая над итерабельным. поэтому xrange()
вызывается ровно один раз, а не "N раз".
В. xrange()
- это имя этой функции в Python 2. она заменяется и переименовывается в range()
в Python 3, поэтому имейте это в виду при портировании. если вы еще не знаете, xrange()
возвращает итератор (-подобный объект), а range()
возвращает списки. так как последний более неэффективен, он устарел в пользу xrange()
, который более удобен для памяти. обходной путь в Python 3, для всех тех, кому нужен список, list(range(N))
.
Ответ 5
Я повторил тест из @Alex Martelli. Идиоматический цикл в 5 раз быстрее, чем цикл while:
python -mtimeit -s'from while_vs_for import while_loop as loop' 'loop(10000)'
10 loops, best of 3: 9.6 sec per loop
python -mtimeit -s'from while_vs_for import for_loop as loop' 'loop(10000)'
10 loops, best of 3: 1.83 sec per loop
while_vs_for.py
def while_loop(N):
x = 0
while x < N:
y = 0
while y < N:
pass
y += 1
x += 1
def for_loop(N):
for x in xrange(N):
for y in xrange(N):
pass
На уровне модуля:
$ time -p python for.py
real 4.38
user 4.37
sys 0.01
$ time -p python while.py
real 14.28
user 14.28
sys 0.01