UpPetto

This is a real course generated by UpPetto — unedited.

AI-generated. Two models wrote it, a third checked every risky claim against sources, a fourth re-checked.

Create my own course

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 элементов ошибку — а получает пустой список.

Срез a[1:4] включает индексы 1, 2, 3 и не включает stop-индекс 4
Срез a[1:4] включает индексы 1, 2, 3 и не включает stop-индекс 4

Частые ошибки переноса привычек из C:

  1. for i in range(len(a)): x = a[i], когда индекс дальше не используется — чистый шум, признак «C с питоновским синтаксисом».
  2. Разворот списка руками циклом от len(a)-1 до 0 вместо a[::-1] или reversed(a).
  3. Ожидание IndexError от a[i:j] и избыточные проверки if j > len(a): j = len(a).
  4. Модификация списка во время прямого перебора (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 при переходе на идиоматичный Python

Разобранный пример

В 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
[verified] Срез никогда не бросает IndexError при выходе границ за пределы коллекции
Стандартная семантика срезов Python; база подтверждает, что срезы заменяют ручные проверки границ
[verified] Фигура (figure-svg): срез a[1:4] включает индексы 1,2,3 и не включает stop-индекс 4
Соответствует синтаксису start:stop:step из базы знаний (stop не включается)
[verified] Фигура (mermaid): цикл for-in получает итератор и завершается по StopIteration
База: любой объект с __iter__ используется в for; StopIteration — штатный сигнал конца итерации
[removed] Подпись в mermaid: 'enumerate(a) — тема следующего урока'
Следующий урок посвящён включениям; enumerate разбирается позже — подпись исправлена на 'enumerate(a)'
[removed] Уровень лога извлекается как log_line[22:log_line.find(']', 22)] при метке времени длиной 21 символ
Арифметически неверно: символ '[' стоит на индексе 22, буква 'I' — на 23; выражение дало бы '[INFO'. Практика заменена
[removed] В mermaid-схеме убрана подпись 'enumerate(a) — тема следующего урока': enumerate разбирается позже, а не в следующем уроке
[removed] Убрана практика из версии B про разбор лог-строки срезами: указанные там смещения (индекс 22 и 'временная метка 21 символ') дают результат '[INFO', а не 'INFO'

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.

Key concepts: for-in slices off-by-one
Tell me more 🔒 Didn't understand — explain simply 🔒 Show examples 🔒 Sources 🔒

On your own course these buttons answer instantly, quizzes track what you've mastered, and lessons adapt to your gaps. Write my course

Check yourself

1. Что произойдёт при выполнении a[10:20] для списка длиной 5?
2. Как идиоматично получить список в обратном порядке?
3. Какая привычка из C-циклов чаще всего порождает off-by-one ошибки и устраняется конструкцией for-in?
On your own course, these are marked as you answer