Операторы цикла
Цикл for, также называемый циклом с параметром, в языке Питон богат возможностями. В цикле for указывается переменная и множество значений, по которому будет пробегать переменная. Множество значений может быть задано списком, кортежем, строкой или диапазоном.
Вот простейший пример использования цикла, где в качестве множества значений используется кортеж:
i = 1
for color in 'red', 'orange', 'yellow', 'green', 'cyan', 'blue', 'violet':
print(i,'-th color of rainbow is ', color, sep = '')
i += 1
В этом примере переменная color последовательно принимает значения ‘red’, ‘orange’ и т.д. В теле цикла выводится сообщение, которое содержит название цвета, то есть значение переменной color, а также номер итерации цикла число, которое сначала равно 1, а потом увеличивается на один (инструкцией i += 1 с каждым проходом цикла.
В списке значений могут быть выражения различных типов, например:
for i in 1, 2, 3, 'one', 'two', 'three':
print(i)
При первых трех итерациях цикла переменная i будет принимать значение типа int, при последующих трех — типа str.
Цикл for и изменение переменных — Введение в программирование
Этот урок будет быстрым и простым, так что пристегнитесь.
Вызовем функцию факториала с циклом while:
const factorial = (n) => let counter = 1; let result = 1; while (counter n) result = result * counter; counter = counter + 1; > return result; >
Когда мы работаем с переменными, мы часто поступаем так: меняем их значения, прибавляя к ним сколько-нибудь или умножая их на что-то. Или просто прибавляем или вычитаем единицу.
Как и во многих других языках программирования в JavaScript есть для этого упрощенные формы.
Вместо result = result * counter вы можете сказать result *= counter . Результат будет тот же самый, это просто способ сократить запись. Точно так же вы можете поступить со знаками плюс, минус и остатком от деления:
Добавление единицы к переменной — тоже очень типичная операция, поэтому вместо counter = counter + 1 можно записать counter++ . Так же для минуса — counter = counter — 1 равносильно counter— . Это операторы инкрементирования и декрементирования.
Есть два вида, проще их понять на примере:
// Postfix let a = 3; let b; b = a++; // b = 3, a = 4 // Prefix let a = 3; let b; b = ++a; // b = 4, a = 4
Если вы поставите ++ после имени переменной — это постфиксная нотация — то фактическое сложение произойдёт после того, как значение вернётся. Вот почему b тут 3: оно получает значение перед тем как меняется a .
Если вы поставите ++ перед именем переменной — это префиксная нотация — то фактическое сложение произойдёт перед тем, как значение вернётся. Вот почему b тут 4: оно получает значение после того как меняется a .
Но в конце в обоих случаях a становится 4.
Это обновлённая функция факториала:
const factorial = (n) => let counter = 1; let result = 1; while (counter n) result *= counter; counter++; > return result; >
Здесь не имеет значения, используем мы префикс или постфикс когда инкрементируем counter , потому что значение не хранится больше нигде.
Этот код немного проще и короче. Иметь цикл — этот повторяющийся код — со счётчиком контролирующим повторения — распространённый в программировании приём. Поэтому кроме цикла while существует цикл for. В нем есть встроенные счетчики.
Это та же функция факториала, но с циклом for вместо цикла while:
const factorial = (n) => let result = 1; for (let counter = 1; counter n; counter++) result *= counter; > return result; >
Здесь три момента:
- Инициализация счётчика.
- Условие цикла. Так же как и в цикле while, этот цикл будет повторяться пока это условие истинно.
- Обновление счётчика. Как менять счётчик в каждом шаге.
А затем следует тело, код, который должен повторяться. Нам не нужно менять счётчик в теле, потому что он будет меняться, благодаря этому выражению сверху.
Пришло время использовать все эти навороченные знания и написать код! Переходите к тесту и упражнению прямо сейчас!
Дополнение к уроку
Скрытые сложности
Операции декремента и инкремента кажутся мощными механизмами, но их использование привносит ненужную сложность в программы. Код, написанный с их использованием, часто превращается в ребус. Попробуйте ответить, чему равно значение:
let x = 5; let y = 10; console.log(x++ + ++y);
Как видите, этот код заставляет думать, так как кроме арифметических выражений, мы имеем дело с побочными эффектами.
Во многих языках таких операций нет в принципе. Линтеры (программы, проверяющие код на соответствие стандартам) в JS настроены так, чтобы «ругаться» при виде этих операций в коде. Вместо них предлагается делать так:
x += 1; // x = x + 1;
Что гораздо проще и понятнее. Да, не получится записать выражение в одну строку, но сам код будет очевидным и без сюрпризов.
Соответствующее правило в eslint: https://eslint.org/docs/rules/no-plusplus
Switch
Конструкция switch может заменить собой несколько условий if . Вот пример обычного условия с if :
let answer; if (num === 1) answer = "One"; > else if (num === 2) answer = "Two"; > else answer = "Nothing"; >
А вот как его можно переписать с помощью switch :
switch(num) case 1: // if (num === 1) answer = "One"; break; case 2: // if (num === 2) answer = "Two"; break; default: answer = "Nothing"; break; >
break необходим, чтобы выйти из блока switch . Если break отсутствует, то выполнение пойдёт ниже по следующим случаям, игнорируя проверки. break также можно использовать в циклах for для мгновенного выхода из цикла.
Если в примере выше убрать все break ‘и, а num будет равен 1, то выполнятся все строки:
answer = "One"; answer = "Two"; answer = "Nothing";
Так что в итоге answer будет иметь значение «Nothing».
Несколько значений case можно группировать.
switch(num) case 1: // if (num === 1) answer = "One"; break; case 2: // if (num === 2) case 3: // if (num === 3) case 4: // if (num === 4) answer = "Two to four"; break; default: answer = "Nothing"; break; >
Выводы
Операторы инкрементирования и декрементирования:
// Postfix let a = 3; let b; b = a++; // b = 3, a = 4 // Prefix let a = 3; let b; b = ++a; // b = 4, a = 4
const factorial = (n) => let result = 1; // initialization↓ condition↓ update↓ for (let counter = 1; counter n; counter++) result *= counter; > return result; >
Тут следует упомянуть о том, что все 3 выражения в цикле for не обязательны.
Например, в блоке инициализации не требуется определять переменные:
let counter = 1; for (; counter n; counter++) // любой код >
Как и блок инициализации, блок условия не обязателен. Если пропустите это выражение, вы должны быть уверены, что прервете цикл где-то в теле, а не создадите бесконечный цикл.
for (let counter = 1;; counter++) if (counter n) break; // любой код >
Вы можете пропустить все 3 блока. Снова убедитесь, что используете break, чтоб закончить цикл, а также изменить счётчик так, чтоб условие для break было истинно в нужный момент.
let counter = 1; for (;;) if (counter >= n) break; // любой код counter++; >
Обратите внимание на то, что если внутри тела цикла использовать оператор return , то выполнение цикла будет прервано и функция вернет значение.
// Функция должна посчитать сумму всех чисел от 1 до n const sum = (n) => let result = 0; for (let counter = 1; counter n; counter++) return 10; // return прерывает цикл result += counter; > return result; > sum(5); // 10 sum(20); // 10 sum(50); // 10
Остались вопросы? Задайте их в разделе «Обсуждение»
Вам ответят команда поддержки Хекслета или другие студенты
Обьявление переменных в цикле python
Реально ли сделать механизм по которому, цикл пробегаться по словарю и объявляет переменную с именем ключа и которой присваивается соответствующее значения из этого же словаря. P.S. нужно для написания модуля который предназначен для реализации мультиязычности в программе, т.е. доступ к соответствующему словарю языка который берется из файла json и выводу из него сообщения по ключу(mess_id).
Отслеживать
задан 9 ноя 2017 в 0:08
67 2 2 серебряных знака 5 5 бронзовых знаков
Python и локальные переменные внутри цикла
Нет ли способа поиметь в Python’е локальные переменные внутри циклов? Такие, которые не будут загромождать область видимости вовне? Иначе, когда код разрастается на экран-другой, понимать, что к чему, становится всё труднее.
Я знаю, что для хорошего кода тела циклов выносят в функции и методы. Однако клепаемый код — это, зачастую, одноразовые скрипты для статистических расчётов. Которые пишутся в духе «посмотрим на графики от предыдущей итерации и добавим/удалим пяток строк». О проектировании часто речь не идёт вообще, потому как я часто не знаю, что буду писать дальше.
Python здесь — это замена MATLABу.
И таки да, нет ли способа сделать так, чтобы переменные внутри циклов были локальными?

Yak ★
25.09.13 17:41:35 MSK
← 1 2 →

Прямых способов нет. Но есть возможность например в начале тела цикла создавать объект пустого класса, дальше напихивать в него поля, а в конце тела цикла его убивать.
AIv ★★★★★
( 25.09.13 17:57:58 MSK )
нет ли способа сделать так, чтобы переменные внутри циклов были локальными?
Есть. Взять вместо прыщеподелки любой вменяемый язык программирования.
anonymous
( 25.09.13 17:58:54 MSK )
А какая разница, локальные они для цикла или локальные для функции. Это же не Си, в Питоне их можно использовать повторно для следующего цикла.
kvap ★
( 25.09.13 18:02:09 MSK )
Ответ на: комментарий от anonymous 25.09.13 17:58:54 MSK

Это например какой же?
Yak ★
( 25.09.13 18:07:31 MSK ) автор топика
Ответ на: комментарий от AIv 25.09.13 17:57:58 MSK

Прямых способов нет. Но есть возможность например в начале тела цикла создавать объект пустого класса, дальше напихивать в него поля, а в конце тела цикла его убивать.
Можете показать кодом?
Yak ★
( 25.09.13 18:14:58 MSK ) автор топика
Ответ на: комментарий от Yak 25.09.13 18:14:58 MSK

class Dummy: pass . for . : ns = Dummy() ns.a = . b = . ns.a . . del ns
ЛИСП же! Не связывайтесь с лавсанчегом, он упорот чуть более чем полностью.
AIv ★★★★★
( 25.09.13 18:20:21 MSK )
Ответ на: комментарий от AIv 25.09.13 17:57:58 MSK
И зачем там объект?
anonymous
( 25.09.13 18:26:36 MSK )
Ответ на: комментарий от AIv 25.09.13 18:20:21 MSK
Это не лавсан, да и упорот он не более иных сиплюсплюсников, явовщиков, хаскелистов, эрлангистов и т.д. (список можно продолжать).
dave ★★★★★
( 25.09.13 18:27:05 MSK )
Ответ на: комментарий от AIv 25.09.13 18:20:21 MSK
А разве ‘with’ не делает как раз примерно того же — объект, который убивается при завершении.
alpha ★★★★★
( 25.09.13 18:29:40 MSK )
Ответ на: комментарий от alpha 25.09.13 18:29:40 MSK

цикл не является выражением.
aedeph_ ★★
( 25.09.13 18:35:14 MSK )
Ответ на: комментарий от aedeph_ 25.09.13 18:35:14 MSK

Это я знаю. Я только не знаю, как его правильно делать выражением. Неужели и правда втыкать класс-затычку как у AIv?
Yak ★
( 25.09.13 18:36:19 MSK ) автор топика
Ответ на: комментарий от Yak 25.09.13 18:36:19 MSK

Можно свой оператор assignment’а написать, возвращающий строчку для exec’а, который будет кроме просто «=» дампать переменные в специальные Set’ы, а также свой for, который будет подчищать за собой.
Но разумеется, так делать не нужно.
aedeph_ ★★
( 25.09.13 18:42:04 MSK )
Сам себе придумал проблему, сам её и решай, мы-то тут при чём.
anonymous
( 25.09.13 18:45:31 MSK )

В Perl’ике перед переменной достаточно поставить my , например.
Наверняка в питоне есть что-то подобное.
makyrros
( 25.09.13 18:47:40 MSK )
Ответ на: комментарий от Yak 25.09.13 18:36:19 MSK
Зачем тебе класс-затычка? Сделай «del a, b, c» после цикла и радуйся.
anonymous
( 25.09.13 18:49:30 MSK )
Ответ на: комментарий от anonymous 25.09.13 18:49:30 MSK

Да, но а если их там с десяток?
Yak ★
( 25.09.13 18:52:05 MSK ) автор топика
Ответ на: комментарий от Yak 25.09.13 18:52:05 MSK
Выдели в функции циклы.
zz ★★★★
( 25.09.13 18:57:31 MSK )
Ответ на: комментарий от aedeph_ 25.09.13 18:35:14 MSK
Я сразу скажу что я чайник, и не понимаю.
Если допустим я сделаю так:
with a() as A: for: do_smth(A)

то для A по окончании будет вызываться __exit__(), который может это A прибивать. И вроде бы это то же самое, что предлагает AIv . Нет?
alpha ★★★★★
( 25.09.13 18:58:17 MSK )
Последнее исправление: alpha 25.09.13 18:58:39 MSK (всего исправлений: 1)
Ответ на: комментарий от zz 25.09.13 18:57:31 MSK

Тогда будет немного тяжелее перекраивать код на ходу.
Yak ★
( 25.09.13 18:58:36 MSK ) автор топика
Ответ на: комментарий от zz 25.09.13 18:57:31 MSK
def mahlopp: for blah mahloop() def mahotherlopp: for blah mahotherlopp()
Можно прямо в теле метода локальные функции делать.
zz ★★★★
( 25.09.13 18:59:41 MSK )
Ответ на: комментарий от zz 25.09.13 18:59:41 MSK
А вообще можно даже одно имя пользовать.
zz ★★★★
( 25.09.13 19:01:30 MSK )
Ответ на: комментарий от Yak 25.09.13 18:58:36 MSK
zz ★★★★
( 25.09.13 19:01:44 MSK )
Переменные внутри циклов и так локальные. Тебе нужен аналог let из лиспа или что? Такого нет. И в питоне это особо вообще никак не мешает — если тебе нужны такие же имена после цикла: вариант с del’ом уже предложили, второй вариант — берешь и переопределяешь переменные.
alienclaster ★★★
( 25.09.13 19:55:53 MSK )

# coding: utf-8 # быдлокодим def foo(): while 1: old_globals = globals().keys() yield new_globals = globals().keys() for var in new_globals: if var not in old_globals: globals().pop(var) yield foo = iter(foo()) # используем next(foo) for i in xrange(5): j = k = i next(foo) # проверяем print i, j, k # профит!
Virtuos86 ★★★★★
( 25.09.13 20:33:56 MSK )
Ответ на: комментарий от alienclaster 25.09.13 19:55:53 MSK

Переменные внутри циклов и так локальные.
Тело цикла не является блоком кода, вводящим отдельное локальное пространство имен. Поэтому переменные в цикле определяются в том же неймспейсе, где определен цикл (то бишь переменная цикла). Если циклы находятся на уровне модуля (как у ТС), то есть в глобальном пространстве имен, то и их переменные глобальны. Такое же поведение у list comprehensions было, поправили не помню в какой версии.
Virtuos86 ★★★★★
( 25.09.13 20:43:38 MSK )
Последнее исправление: Virtuos86 25.09.13 20:45:17 MSK (всего исправлений: 1)
Ответ на: комментарий от Virtuos86 25.09.13 20:33:56 MSK

опять же, на немного лохматых версиях питоши вместо
Virtuos86 ★★★★★
( 25.09.13 20:49:44 MSK )
Ответ на: комментарий от Virtuos86 25.09.13 20:33:56 MSK

Неплохо, однако предыдущее состояние переменных он не возвращает, только выкидывает новые.
aedeph_ ★★
( 26.09.13 00:05:46 MSK )
Как вариант делай приставку к переменным local_ Только не забывай их обнулять. Будешь знать что в local_ нету ничего важного и не страшно если переменная с таким именем уже встречалась. Как я понял тебе же главное не обратится к уже существующей переменной или не переопределить уже существующею? тогда прокатит. Помоему самый простой вариант.
local_num, local_f = '', ''
matrixd ★
( 26.09.13 02:57:22 MSK )
ТС, тебе не стыдно? Смотри сколько говнокода уже сгенерировано. Если портянка большая, то ответ напрашивается сам собой — разбить. Если это одноразовая большая портянка, то опять всё очевидно — забить. Проблема яйца выеденного не стоит.
anonymous
( 26.09.13 03:02:27 MSK )
Ответ на: комментарий от aedeph_ 26.09.13 00:05:46 MSK

Предыдущее? Хех, тогда только в функции циклы выносить, иначе костыли будут шикарные.
import copy a, b, c = 1, 2, 3 code = '''\ a, b, c = 4, 5, 6 ''' exec code in copy.deepcopy(globals()) print a, b, c
Код не проверял, не на чем. Но сомнительно, что глубокое копирование будет хорошо работать на реальных пользовательских объектах, как на сферических примерах.
Virtuos86 ★★★★★
( 26.09.13 04:34:40 MSK )
Ответ на: комментарий от Virtuos86 25.09.13 20:43:38 MSK
Тело цикла не является блоком кода, вводящим отдельное локальное пространство имен. Поэтому переменные в цикле определяются в том же неймспейсе, где определен цикл (то бишь переменная цикла).
Да, действительно — это я фигню сказал, хотя сказал там же, что let отсутствует.
Если циклы находятся на уровне модуля (как у ТС), то есть в глобальном пространстве имен, то и их переменные глобальны.
Не очень могу представить, зачем в библиотеки циклам бы находиться внутри модуля, а не в функции / классе.
Такое же поведение у list comprehensions было, поправили не помню в какой версии
alienclaster ★★★
( 26.09.13 05:03:59 MSK )
Ответ на: комментарий от alienclaster 26.09.13 05:03:59 MSK

Не очень могу представить, зачем в библиотеки циклам бы находиться внутри модуля, а не в функции / классе.
ТС написал, зачем ему. Хотя настоящий ответ — лень, первейший источник быдлокода :->.
Но-но, без розжига. Не все так однозначно. Те же генераторные выражения такой болезнью изначально не страдали. А l.c. это практически сахар для цикла for с накопителем, потому и копирует его поведение. А оно вот такое. Короче, один из python tips.
Virtuos86 ★★★★★
( 26.09.13 05:32:45 MSK )
Такие, которые не будут загромождать область видимости вовне? Иначе, когда код разрастается на экран-другой, понимать, что к чему, становится всё труднее.
нихрена себе трудней понимать. То, каким образом это решается в питоне намного усложнит код, чем если б ты просто с другой стороны подошел к решению.
P.S. за тролленг питона разве что зачёт. «Код разрастается» — ну, нашел причину)
anonymous
( 26.09.13 05:38:38 MSK )
Ответ на: комментарий от Virtuos86 26.09.13 04:34:40 MSK

Не не не, никакого deepcopy, это полный Содом. Изменение мутабельных (мутация того же объекта) трогать не надо, его могли, и это даже очень вероятно, изменять сознательно. А вот новый assignment в блоке, который затёр старый, следует откатить. Разумеется для питона этого сделать без переопределения (в голове, хотя бы) «=» не выйдет, поэтому и быдлоязычок, и говноскоупинг.
def x(): a = False def y(): print a a = True return y
aedeph_ ★★
( 26.09.13 06:31:35 MSK )
Ответ на: комментарий от Virtuos86 26.09.13 05:32:45 MSK

это практически сахар для цикла for с накопителем
А почему не сахар над list(x for x in iterable)?
aedeph_ ★★
( 26.09.13 06:33:41 MSK )
Ответ на: комментарий от aedeph_ 26.09.13 06:33:41 MSK

Потому что list(x for x in iterable) это сахар над list((x for x in iterable)), а генераторные выражения появились намного позднее генераторов списков.
Virtuos86 ★★★★★
( 26.09.13 10:12:00 MSK )
Последнее исправление: Virtuos86 26.09.13 10:12:41 MSK (всего исправлений: 1)
Ответ на: комментарий от aedeph_ 26.09.13 06:31:35 MSK

Теперь понял. Однако, в питоне есть блоки кода — это функции, определяемые инструкциями «def» и «lambda». Если нужна функциональность блоков кода, то надо использовать их, а не пенять, что вещи, не предназначенные для этого, не удается использовать не по назначению. Теперь что касается переопределения «=». Есть два пути. Первый, чернокнижный: можно написать свой кодек, который при парсинге и разборе исходника будет запоминать определяемые переменные и бить по рукам при их повторном переопределении. Все это отслеживается на этапе даже не «компиляции», а чуть пораньше, и динамику не учитывает. Не говоря уже о том, что это уже и не питон будет. Кодек — это то, что указывается в модуле как
В принципе, для себя вполне приемлемый вариант.
Второй: наподобие того, что предложил AIv. Пишем свой класс-неймспейс, переопределяем для него __getattribute__/__setattr__, создаем инстанс этого класса (назовем его «let»), переменные регистрируем как атрибуты let. Дальше контролируем доступ к ним как угодно. Я такое видел, имхо неизящно и избыточно.
Virtuos86 ★★★★★
( 26.09.13 10:48:45 MSK )
Ответ на: комментарий от Virtuos86 26.09.13 10:12:00 MSK

Странно, всегда считал, что genexpr появился за одну минорную версию до lc, но был неправ, 2.0 (2000) vs 2.4 (2002)
