Выбор правильной конструкции цикла
Python предлагает множество конструкций для выполнения циклов. В этой статье они представлены и даны советы по их конкретному использованию. Кроме того, мы также рассмотрим производительность каждой конструкции цикла в вашем коде Python. Это может быть для вас неожиданностью.
Петли, петли, петли
Язык программирования обычно состоит из нескольких типов базовых элементов, таких как присваивания, операторы и циклы. Идея цикла состоит в том, чтобы повторять отдельные действия, указанные в теле цикла. Распространены разные виды петель:
- пока заданное условие истинно (пока условие do sth.)
- пока не будет выполнено определенное условие (выполните что-то до выполнения условия)
- для фиксированного количества шагов (итераций) (для / от 'x' до 'y' сделать что-то.)
- бесконечный цикл и выход / прерывание по условию (в то время как условие1 выполняет sth. и выходит по условию2)
Конструкции цикла, поддерживаемые Python
Python поддерживает часть названных выше конструкций, а также предлагает уникальные расширения для упомянутых нами типов.
Базовые циклы while
while condition:
statements
До тех пор , как «условия» соблюдены все утверждения в теле в while
цикла выполняются по крайней мере один раз. После каждого выполнения
операторов условие повторно оценивается. Написание цикла выглядит так:
Листинг 1
fruits = ["banana", "apple", "orange", "kiwi"]
position = 0
while position < len(fruits):
print(fruits[position])
position = position + 1
print("reached end of list")
Этот код выведет один элемент списка после следующего:
banana
apple
orange
kiwi
reached end of list
while
Циклы с условием else
Эта конструкция специфична для языка Python, но весьма полезна:
while condition:
statements
else:
statements
Это в while
цикл действует аналогично обычной в while
цикла,
введенные ранее. Операторы в части else
выполняются, как только
условие перестает выполняться. Например, если достигнут конец списка,
как в нашем предыдущем примере. Вы можете интерпретировать это как
then
, если условие цикла больше не встречались.
Листинг 2
fruits = ["banana", "apple", "orange", "kiwi"]
position = 0
while position < len(fruits):
print(fruits[position])
position = position + 1
else:
print("reached end of list")
Это выведет один элемент списка после следующего, а также дополнительный
текст из print
в предложении else:
banana
apple
orange
kiwi
reached end of list
Этот вид цикла с предложением else
удобен для вывода сообщений или
выполнения операторов в случае сбоя вашего условия.
Одна важная вещь , чтобы отметить , что else
условие не выполняется
, если break
из в while
цикл или если ошибка возникает внутри в
while
цикла.
Бесконечные циклы while
Бесконечные циклы всегда преподаются как критические компоненты, и их следует избегать, если условие разрыва является сложным. Хотя бывают случаи, когда бесконечные циклы помогают элегантно писать код.
Вот лишь несколько вариантов использования бесконечных циклов:
- устройства, которые пытаются поддерживать сетевые соединения активными, например точки беспроводного доступа
- клиенты, которые пытаются постоянно обмениваться данными с хост-системой, например с сетевой файловой системой (NFS или Samba / CIFS)
- игровые циклы для рисования и обновления состояния игры
|
|
while True:
if condition:
break
statements
Имейте в виду, что операторы в теле бесконечного цикла выполняются хотя бы один раз. Вот почему я рекомендую писать условие прерывания как самый первый оператор после заголовка цикла. Следуя нашему примеру кода, бесконечный цикл выглядит следующим образом:
Листинг 3
fruits = ["banana", "apple", "orange", "kiwi"]
position = 0
while True:
if position >= len(fruits):
break
print(fruits[position])
position = position + 1
print("reached end of list")
for
циклов с итератором
Работа со списками описывается как использование ключевого слова for
в
сочетании с итератором. Псевдокод выглядит следующим образом:
for temp_var in sequence:
statements
Это упрощает код Python для обработки нашего списка следующим образом:
Листинг 4
fruits = ["banana", "apple", "orange", "kiwi"]
for food in fruits:
print(food)
print("reached end of list")
В этом типе конструкции цикла интерпретатор Python обрабатывает итерацию по списку и заботится о том, чтобы цикл не выходил за пределы диапазона списка. Имейте в виду, что операторы в теле цикла запускаются один раз для каждого элемента в списке - независимо от того, один это один или двадцать тысяч.
Если список пуст, операторы в теле цикла не выполняются. Изменение
списка с точки зрения добавления или удаления элементов внутри цикла
for
может запутать интерпретатор Python и вызвать проблемы, поэтому
будьте осторожны.
for
циклов с итератором и предложением else
Аналогично в while
цикл, Python также предлагает else
заявление для
for
цикла. Он работает аналогично и может интерпретироваться как
then
, так же, как и раньше. Псевдокод выглядит следующим образом:
for temp_var in sequence:
statements
else:
statements
Используя это ключевое слово, наш код изменяется следующим образом:
Листинг 5
fruits = ["banana", "apple", "orange", "kiwi"]
for food in fruits:
print(food)
else:
print("reached end of list")
Неподдерживаемые конструкции цикла
Как было сказано в начале, существует много разных стилей петель. Однако
Python не поддерживает их все. Python не поддерживает цикл do-until
foreach
, как, возможно, известно из PHP. Такие случаи решаются с
помощью in
который создает довольно привлекательный код, если вы с ним
знакомы. См. Альтернативные способы написания цикла сверху.
Какую петлю выбрать?
В общем случае while condition
требуют, чтобы условие было указано
перед операторами цикла. Это может привести к тому, что операторы в теле
цикла никогда не будут выполнены. Кроме того, не всегда ясно, сколько
раз цикл будет выполняться для циклов while
Вместо этого for
циклы
сосредотачиваются на итераторе, который указывает, как часто выполняются
операторы в теле цикла.
Рекомендуется использовать for
если вы точно знаете количество
элементов, которые нужно перебрать. В противоположность этому , в
while
петля лучше , если у вас есть логическое выражение evalutate, а
не список элементов для цикла по.
Повышение качества вашего кода
Многие молодые программисты не всегда заботятся о качестве своего кода, в основном потому, что они выросли в то время, когда никто не должен думать о памяти и мощности процессора - у нас их просто много на современных компьютерах. Вместо этого более опытные (также известные как «старые») разработчики более склонны оптимизировать свой код в максимально возможной степени и могут помнить о подсчете инструкций ЦП и количества используемых ячеек памяти.
Итак, что означает качество сегодня? С точки зрения эффективности он охватывает написание минимального количества кода, насколько это возможно, и эффективное выполнение кода - только столько инструкций процессора, сколько необходимо. Во-первых, с сегодняшними интерпретаторами, средами выполнения и фреймворками довольно сложно рассчитать это должным образом, а во-вторых, это всегда компромисс между этими двумя показателями. Ключевой вопрос заключается в том, как часто этот код будет использоваться и сколько времени мы должны потратить на его оптимизацию, чтобы выиграть несколько микросекунд процессорного времени.
В качестве примера мы рассмотрим for
выполняющий итерацию по списку.
Обычно мы пишем это так:
Листинг 6
for entry in range(0, 3):
print(entry)
Это выводит значения 0, 1 и 2. Метод range()
создает iterable
[0, 1, 2]
каждый раз, когда оценивается заголовок цикла. Поэтому лучше
писать так:
Листинг 7
entryRange = range(0, 3)
for entry in entryRange:
print(entry)
Хотя для данного примера это может показаться не очень оптимизационным, подумайте, был ли диапазон от 0 до 1000000 или более. По мере роста нашего списка мы экономим больше времени, и наш код выполняется быстрее.
Кроме того, эти утверждения могут быть выражены как в while
цикла:
Листинг 8
entryRange = range(0, 3)
index = 0
while index < len(entryRange):
print(entryRange[index])
index = index + 1
К этому моменту даже использование функции range()
кажется
бессмысленным. Вместо этого мы могли бы просто использовать константу
для условного index
и индекса в качестве счетчика для условного
выражения и печати:
index = 0
while index < 3:
print(index)
index = index + 1
Подобные небольшие оптимизации могут обеспечить небольшое улучшение производительности ваших циклов, особенно когда количество итераций становится очень большим.
Тесты производительности
До сих пор мы говорили о коде цикла и о том, как его правильно написать. Тест производительности может помочь пролить свет. Идея любезно позаимствована из интересной статьи в блоге Неда Батчелдера [1].
Используется perf
, который выполняет тесты производительности для
исполняемого программного кода [2]. Базовый вызов - это
perf stat program
тогда как stat
сокращает статистику, а программа -
это вызов, который мы хотели бы оценить. Для тестирования наших
вариантов цикла были сделаны следующие вызовы:
Листинг 9
perf stat python3 while-1.py
perf stat python3 while-2.py
perf stat python3 while-3.py
perf stat python3 for-4.py
perf stat python3 for-5.py
perf stat python3 for-6.py
perf stat python3 for-7.py
perf stat python3 while-8.py
Это среднее значение для 10 запусков из-за разницы в загрузке ядра Linux. В следующей таблице показаны результаты:
Тема Листинг 1 Листинг 2 Листинг 3 Листинг 4 Листинг 5
часы задачи (мсек) 20.160077 18,535264 15,975387 15.427334 15.503672 переключатели контекста 10 11 10 13 10 миграции ЦП 0 0 2 1 1 ошибки страницы 851 849 855 848 851 циклы 41 915 010 44 938 837 44 403 696 42 983 392 42 489 206 инструкции 46 833 820 46 803 187 46 926 383 46 596 667 46 701 350
Для листингов 6-8 это выглядит следующим образом:
Тема Листинг 6 Листинг 7 Листинг 8
часы задачи (мсек) 16,480322 18.193437 15.734627 переключатели контекста 9 11 11 миграции ЦП 0 0 1 ошибки страницы 850 851 853 циклы 42 424 639 42 569 550 43 038 837 инструкции 46 703 893 46 724 190 46 695 710
Заключение
Python предлагает разные способы повторения действий и написания циклов записи. Существуют варианты для каждого конкретного случая использования. Наши тесты показали, что циклы находятся в одном измерении с небольшими различиями, а оптимизация интерпретатора Python неплохая.
Ссылки и ссылки
- [1] Нед Батчелдер: Сколько инструкций в заявлении для печати?,
Июль.
2013 г. - [2] Пакет Debian linux-perf
Благодарности
Автор благодарит Герольда Рупрехта и Мэнди Ноймайер за их поддержку и комментарии при подготовке этой статьи.