Обоснование предпочтения Python для синтаксиса - PullRequest
11 голосов
/ 10 апреля 2010

Каково обоснование рекомендуемого использования циклических конструкций в стиле for i in xrange(...) в 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.

Ответы [ 5 ]

22 голосов
/ 10 апреля 2010

Вот правильное сравнение, например в 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. Я надеюсь, что это заставит вас пересмотреть свою ошибочную пропаганду.

11 голосов
/ 10 апреля 2010

Вы забыли сбросить 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

изд: 20.63 с после исправления против 6.97 с использованием xrange

3 голосов
/ 10 апреля 2010

подходит для перебора структур данных

Синтаксис for i in ... отлично подходит для перебора структур данных. На языке более низкого уровня вы, как правило, выполняете итерации по массиву, проиндексированному с помощью int, но с помощью синтаксиса python вы можете исключить этап индексации.

1 голос
/ 11 апреля 2010

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

A. что-то не так с одним из операторов OP, который еще никто не исправил (да, в дополнение к ошибке в коде сброса y):

"Я думаю, это во многом связано с вызовом xrange N раз ...."

в отличие от традиционного подсчета for циклов, Python больше похож на оболочку foreach ... с циклическим повторением. следовательно, xrange() называется ровно один раз , а не "N раз".

B. xrange() - это название этой функции в Python 2. Она заменяет и переименовывается в range() в Python 3, поэтому помните об этом при портировании. если вы еще не знали, xrange() возвращает итератор (-подобный объект), а range() возвращает списки. поскольку последняя более неэффективна, она устарела в пользу xrange(), что более благоприятно для памяти. Обходной путь в Python 3 для всех, кому нужно иметь список, - list(range(N)).

0 голосов
/ 10 апреля 2010

Я повторил тест из @ ответа Алекса Мартелли . Идиоматический цикл for в 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...