for-in и срезы вместо for(i=0;i<n;i++)
core переписать любой индексный цикл по массиву в идиоматичную итерацию for-in или срез
Индексный цикл for (i = 0; i < n; i++) в C — это не просто способ перебора, а единственный способ: массив в C не «умеет» отдавать свои элементы сам, вы обязаны вычислять адрес каждого элемента через индекс. В Python любой объект, реализующий протокол итерации (__iter__), умеет отдавать себя поэлементно — и for item in collection использует это напрямую, без переменной-счётчика.
Практическое следствие: класс ошибок off-by-one (<= вместо <, забытый -1) в идиоматичном Python-коде почти исчезает, потому что вы никогда не пишете границу вручную.
/* C: типичное место для off-by-one */
for (int i = 0; i <= n; i++) { /* здесь баг: должно быть < */
process(arr[i]);
}
# Python: границы не существуют как отдельная сущность
for item in items:
process(item)
graph TD
subgraph C_style["Цикл в стиле C"]
A["i = 0"] --> B{"i < len(items)?"}
B -- "да" --> C["item = items[i]"]
C --> D["обработать item"]
D --> E["i = i + 1"]
E --> B
B -- "нет" --> F["Конец"]
end
subgraph Pythonic["Идиоматичный for-in"]
G["получить итератор из items"] --> H{"есть следующий элемент?"}
H -- "да" --> I["item = next(итератор)"]
I --> J["обработать item"]
J --> H
H -- "нет: StopIteration" --> K["Конец"]
endВторой мощный инструмент — срезы, синтаксис a[start:stop:step]. Срез возвращает новую последовательность (для списка — поверхностную копию) и делает то, для чего в C вы писали бы отдельный цикл с ручными границами:
| Задача | C (псевдокод) | Python со срезами |
|---|---|---|
| Скопировать массив | for(i=0;i<n;i++) new[i]=old[i]; |
new_list = old_list[:] |
| Первые 5 элементов | for(i=0;i<5;i++) ... |
my_list[:5] |
| Всё, кроме первых двух | for(i=2;i<n;i++) ... |
my_list[2:] |
| Развернуть | for(i=0;i<n/2;i++) swap(...) |
my_list[::-1] |
| Каждый второй | for(i=0;i<n;i+=2) ... |
my_list[::2] |
При этом срез никогда не бросает IndexError, даже если границы выходят за пределы коллекции — он просто возвращает столько элементов, сколько есть. Это отличается и от прямой индексации a[10], которая бросает исключение, и от C, где выход за границы массива — undefined behavior. C-программист интуитивно ждёт от a[10:20] на списке из 5 элементов ошибку — а получает пустой список.
Частые ошибки переноса привычек из C:
for i in range(len(a)): x = a[i], когда индекс дальше не используется — чистый шум, признак «C с питоновским синтаксисом».- Разворот списка руками циклом от
len(a)-1до0вместоa[::-1]илиreversed(a). - Ожидание
IndexErrorотa[i:j]и избыточные проверкиif j > len(a): j = len(a). - Модификация списка во время прямого перебора (
for x in a: a.remove(x)) — итератор списка не защищён от изменения длины, элементы будут пропущены; в отличие от инвалидации итераторов в C++, здесь это тихая логическая ошибка, а не крах.
flowchart LR
A["for i in range(len(a)): a[i]"] -->|убрать индекс| B["for x in a: ..."]
A -->|нужен подмассив| C["a[start:stop:step]"]
B -->|нужен и индекс, и значение| D["enumerate(a)"]Разобранный пример
В C вы бы скопировали часть массива примерно так:
int result[MAX];
int j = 0;
for (int i = 2; i < n - 1; i++) {
result[j++] = arr[i];
}
Три потенциальных источника ошибок: граница n - 1, начальный индекс 2, отдельный счётчик j. В Python это одна строка без ручных границ:
result = arr[2:-1]
-1 как индекс — это «второй с конца», ещё один способ не вычислять n - 1 руками. Срез сам решает, сколько элементов взять; вам остаётся сформулировать что вы хотите, а не как дойти до этого индексами.
Попробуй сейчас
Перепишите без единой переменной-индекса и без range(len(...)):
def every_second_reversed(a):
result = []
i = len(a) - 1
while i >= 0:
result.append(a[i])
i -= 2
return result
Ожидаемый результат: для [1,2,3,4,5,6] функция возвращает [6,4,2], а ваше решение — один срез с тремя параметрами start:stop:step, без цикла.
Получилось, если…
Вы справились, если получили a[::-2] (или эквивалент) и можете объяснить, почему это работает даже для пустого списка и списка из одного элемента без отдельных проверок границ — в отличие от ручной версии, где понадобилась бы проверка len(a) == 0.
Вывод
Прямая итерация for x in a и срезы a[start:stop:step] — не «короткий синтаксис» индексного цикла: они устраняют саму переменную-индекс как источник off-by-one, а срез никогда не бросает IndexError при выходе за границы, в отличие от прямой индексации.
AI-generated · source-grounded review
🛡 Fact-checked: 3 risky claims verified · 2 removed · confidence: high · figures: 3
A second model re-checked this lesson's claims and corroborated 3 of 5 , and could not confirm 2 either way. This is incomplete corroboration, not a disagreement.
On your own course these buttons answer instantly, quizzes track what you've mastered, and lessons adapt to your gaps. Write my course