⚖️ Старый и современный подход

⚡ Различия в двух словах

Классический: ручная индексация через range(len(...)) и конкатенация строк — понятно новичкам, но многословно.

Современный: прямая итерация по элементам, enumerate() для индексов, zip() для пар последовательностей, f-strings для вывода.

Вывод: современный стиль короче, безопаснее и читаемее. Классический подход с индексами оставьте только там, где он действительно нужен.

📜 Классический подход с ручными индексами

Во многих учебных материалах новичкам показывают перебор строки по индексам. Это работает, но не является Pythonic-стилем.

📄 examples/classic_index_loop.py
text = "Python"
for i in range(len(text)):
    letter = text[i]
    print(letter)

Что здесь происходит: мы вручную генерируем индексы от 0 до len(text) - 1, а затем по каждому индексу извлекаем символ. Это лишние действия и лишние возможности для ошибок.

❌ Почему этот подход может быть не лучшим сейчас

  • Лишние индексы: если вам нужны сами элементы, индексы избыточны и отвлекают.
  • Ошибки off-by-one: ручной подсчёт границ range(len(...)) легко сломать.
  • Менее читаемо: код читается как «для каждого индекса…», а не «для каждой буквы…».
  • Конкатенация строк: "Value: " + str(i) медленнее и многословнее f-string.

✅ Рекомендуемый современный вариант

1. Прямая итерация по строке

📄 examples/modern_string_loop.py
text = "Python"
for letter in text:
    print(letter)

Что улучшилось: код говорит сам за себя — «для каждой буквы в тексте».

2. enumerate() для индексов

Когда индекс всё-таки нужен, используйте enumerate() вместо range(len(...)).

📄 examples/modern_enumerate.py
text = "Python"
for index, letter in enumerate(text):
    print(f"{index}: {letter}")

3. zip() для параллельного перебора

📄 examples/modern_zip.py
names = ["Anna", "Bob", "Charlie"]
ages = [25, 30, 35]

for name, age in zip(names, ages):
    print(f"{name} is {age} years old")

4. f-strings вместо конкатенации

📄 examples/modern_fstrings.py
# Classic
print("Letter: " + letter + ", index: " + str(index))

# Modern
print(f"Letter: {letter}, index: {index}")
Преимущества современного стиля:
  • Меньше индексов — меньше off-by-one ошибок.
  • Код читается как описание задачи, а не как управление счётчиком.
  • enumerate() и zip() работают с любыми итерируемыми объектами.
  • f-strings быстрее и нагляднее конкатенации.

🕰️ Когда индексы всё ещё нужны

Прямая итерация предпочтительна, но индексы уместны, когда:

  • Нужно изменить элементы изменяемой последовательности по позиции.
  • Алгоритм явно оперирует позициями, а не значениями (например, сортировка).
  • Работаем с несколькими последовательностями по одним и тем же индексам.

Даже в этих случаях enumerate() часто делает код чище, чем range(len(...)).

🔧 Как распознать устаревший код

В legacy-проектах можно встретить конструкции вида:

for i in range(len(data)):
    item = data[i]
    # ...

Если i используется только для доступа к data[i], безопасно замените на:

for item in data:
    pass  # replace with real logic

Если индекс тоже нужен, используйте:

for i, item in enumerate(data):
    pass  # replace with real logic