💻 Практические примеры

⚡ Шпаргалка: список за 30 секунд

my_list = [1, 2, 3]
my_list.append(4)
print(my_list[0], my_list[-1])   # 1 4
print(my_list[1:3])              # [2, 3]
for item in my_list:
    print(item)                  # 1 / 2 / 3 / 4 — каждое на своей строке
СинтаксисЧто делаетПример
[] / list()создать пустой список[]
my_list[i] / my_list[-1]элемент по индексу / с конца[10, 20, 30][-1] → 30
my_list[a:b:c]срез — часть списка без изменения исходного[0,1,2,3][1:3] → [1, 2]
for item in my_list:перебор всех элементов по порядку—
append / insert / extend / remove / popметоды изменения списка[1, 2].append(3) → [1, 2, 3]
Топ-3 ошибки: индекс len(my_list) вместо len(my_list)-1 · in ищет весь элемент, не подстроку · append(list) вкладывает список целиком — нужен extend.

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

Пример 1. Создание списка: литерал, list() и пустой список

Список — самый частый способ хранить набор значений. Литерал [...] используют чаще всего, а list() удобен, когда список нужно построить из другой последовательности — строки или range.

📄 examples/creating_lists.py
literal_list = [1, 2, 3, 4, 5]
empty_list_1 = []
empty_list_2 = list()
from_string = list("python")
from_range = list(range(0, 10, 2))

print(literal_list)
print(empty_list_1, empty_list_2)
print(from_string)
print(from_range)
# [1, 2, 3, 4, 5]
# [] []
# ['p', 'y', 't', 'h', 'o', 'n']
# [0, 2, 4, 6, 8]

Что происходит: [] и list() — два равноправных способа получить пустой список. list() с аргументом требует iterable (объект, по которому можно пройти циклом) и превращает его элементы в элементы нового списка.

Пример 2. Список хранит элементы любых типов

В отличие от массивов в некоторых других языках, список Python не привязан к одному типу данных — в нём могут соседствовать числа, строки и даже другие списки.

📄 examples/mixed_types.py
mixed = [42, "Python", 3.14, True, [1, 2, 3]]
print(mixed)
print(type(mixed[0]), type(mixed[1]), type(mixed[4]))
# [42, 'Python', 3.14, True, [1, 2, 3]]
# <class 'int'> <class 'str'> <class 'list'>

Почему так: список хранит не значения «впритык», а ссылки на объекты. Поэтому пятым элементом свободно становится другой список — с ним можно работать как с вложенной структурой (см. пример 11).

Пример 3. Индексация: с начала и с конца

Каждый элемент списка доступен по своему порядковому номеру — индексу. Индексация начинается с 0, а отрицательные индексы отсчитывают позиции с конца без вычисления len(my_list) - 1.

📄 examples/list_indexing.py
fruits = ["apple", "banana", "cherry", "date"]

print(fruits[0])    # apple
print(fruits[3])    # date
print(fruits[-1])   # date
print(fruits[-4])   # apple

Что происходит: fruits[3] и fruits[-1] указывают на один и тот же последний элемент — просто отсчёт идёт с разных сторон. Индекс -1 всегда «последний», независимо от длины списка.

Пример 4. Срезы: [start:stop:step]

Срез возвращает новый список — часть исходного — не изменяя сам исходный список. start включается, stop — нет; отрицательный step идёт справа налево.

📄 examples/list_slicing.py
numbers = [0, 1, 2, 3, 4, 5, 6]

print(numbers[1:4])     # [1, 2, 3]      — с индекса 1 по 3
print(numbers[::2])     # [0, 2, 4, 6]   — каждый второй элемент
print(numbers[::-1])    # [6, 5, 4, 3, 2, 1, 0] — список в обратном порядке
print(numbers[-4:-1])   # [3, 4, 5]      — отрицательные границы
print(numbers[:])       # [0, 1, 2, 3, 4, 5, 6] — полная копия списка

Почему так: пропущенные границы означают «до самого края»: start по умолчанию 0, stop — конец списка. numbers[::-1] — стандартная идиома «перевернуть список», а numbers[:] — стандартная идиома «сделать поверхностную копию».

Пример 5. Изменение элемента по индексу и по срезу

Список изменяемый: можно заменить один элемент по индексу или сразу несколько элементов через срез — причём длина списка при этом может измениться.

📄 examples/list_modifying.py
items = ["pen", "pencil", "marker", "eraser"]
items[1] = "brush"
print(items)   # ['pen', 'brush', 'marker', 'eraser']

items[2:4] = ["ruler", "sharpener", "glue"]
print(items)   # ['pen', 'brush', 'ruler', 'sharpener', 'glue']

Что происходит: items[1] = "brush" заменяет ровно один элемент. А items[2:4] = [...] заменяет диапазон элементов на новый список любой длины — двух элементов среза ("marker" и "eraser") не стало, вместо них добавились три, и список вырос.

Пример 6. len(), конкатенация + и повторение *

+ объединяет два списка в новый, * повторяет список нужное число раз, len() считает элементы. Отдельно стоит поведение при повторении списков со вложенными изменяемыми элементами.

📄 examples/list_operations.py
list1 = [1, 2, 3]
list2 = [4, 5, 6]
combined = list1 + list2
print(combined)   # [1, 2, 3, 4, 5, 6]
print(len(combined))   # 6

repeated = [1, 2, 3] * 2
print(repeated)   # [1, 2, 3, 1, 2, 3]

matrix = [[0, 0]] * 3
matrix[0][0] = 9
print(matrix)   # [[9, 0], [9, 0], [9, 0]] — изменилась КАЖДАЯ строка

Почему так: [[0, 0]] * 3 не создаёт три независимых списка — все три элемента ссылаются на один и тот же вложенный список. Изменение matrix[0][0] видно во всех «строках» сразу, потому что физически это один объект. Для независимых строк нужен [[0, 0] for _ in range(3)] — этот приём подробно разбирает урок 28 «Списки и память».

Пример 7. Проверка вхождения: in и not in

Оператор in проверяет, есть ли значение среди элементов списка, и возвращает True/False. Для строковых элементов он ищет полное совпадение, а не вхождение подстроки.

📄 examples/membership_check.py
numbers = [1, 2, 3, 4, 5]
print(3 in numbers)       # True
print(6 in numbers)       # False
print(6 not in numbers)   # True

fruits = ["apple", "banana", "cherry"]
print("apple" in fruits)  # True
print("app" in fruits)    # False — ищет целый элемент, а не часть строки

Что происходит: "app" in fruits возвращает False, потому что среди элементов списка нет элемента, равного строке "app" — есть только "apple". Чтобы найти элементы, содержащие подстроку, нужно перебрать список и применить in к каждой строке отдельно.

Пример 8. Перебор циклом for и вложенный цикл

Цикл for item in my_list: последовательно берёт каждый элемент. Если элементы сами являются последовательностями (например, строками), внутри можно добавить второй, вложенный цикл.

📄 examples/nested_loop_words.py
words = ["cat", "dog", "bird"]

for word in words:
    print(f"Word: {word}")
    for letter in word:
        print(letter, end=" ")
    print()
# Word: cat
# c a t
# Word: dog
# d o g
# Word: bird
# b i r d

Что происходит: внешний цикл перебирает слова списка, внутренний — буквы текущего слова (строка тоже перебираема). Пустой print() после внутреннего цикла переводит строку перед следующим словом.

Пример 9. Методы append, insert, extend

Список меняют не только через индексы и срезы, но и через методы. append добавляет один элемент в конец, insert — элемент на нужную позицию, extend — все элементы другого списка по отдельности.

📄 examples/list_methods_add.py
basket = ["apple", "banana"]
basket.append("cherry")
print(basket)   # ['apple', 'banana', 'cherry']

basket.insert(1, "kiwi")
print(basket)   # ['apple', 'kiwi', 'banana', 'cherry']

more_fruits = ["fig", "grape"]
basket.extend(more_fruits)
print(basket)   # ['apple', 'kiwi', 'banana', 'cherry', 'fig', 'grape']

basket_2 = ["apple", "banana"]
basket_2.append(more_fruits)
print(basket_2)   # ['apple', 'banana', ['fig', 'grape']] — список добавлен ЦЕЛИКОМ

Почему так: append(more_fruits) кладёт сам объект more_fruits как один элемент — получается вложенный список. extend(more_fruits) вместо этого добавляет по одному элементу more_fruits в конец. Это самая частая путаница у новичков с методами списков.

⚠️ Проверить по документации: методы append/insert/extend/remove/pop (примеры 9–10) выходят за рамки исходного занятия — оно останавливалось на срезах и индексах. Полный разбор всех методов списка — в уроке 26 «Методы списков» и в официальной документации.

Пример 10. Методы remove и pop

remove удаляет первое найденное совпадение по значению, pop удаляет и возвращает элемент по индексу (по умолчанию — последний).

📄 examples/list_methods_remove.py
colors = ["red", "green", "blue", "green"]
colors.remove("green")
print(colors)   # ['red', 'blue', 'green'] — удалилось первое вхождение

last = colors.pop()
print(last, colors)   # green ['red', 'blue']

first = colors.pop(0)
print(first, colors)   # red ['blue']

try:
    colors.remove("yellow")
except ValueError as error:
    print("ValueError:", error)
# ValueError: list.remove(x): x not in list

Что происходит: в списке было два "green", remove убрал только первый по порядку. pop() без аргумента снимает элемент с конца — удобно для стека. Попытка remove несуществующего значения кидает ValueError, а не молча ничего не делает.

Пример 11. Вложенные списки: список списков

Элементом списка может быть другой список — так получаются структуры вроде таблицы или матрицы. Доступ к «глубокому» элементу — через два индекса подряд.

📄 examples/nested_lists.py
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
print(matrix[0])      # [1, 2, 3] — целая строка
print(matrix[1][2])   # 6 — строка 1, столбец 2

for row in matrix:
    print(row)
# [1, 2, 3]
# [4, 5, 6]
# [7, 8, 9]

Почему так: matrix[1] возвращает вложенный список [4, 5, 6], а второй индекс [2] уже применяется к нему. Как устроено копирование таких структур и почему поверхностная копия (matrix[:]) здесь не спасает — в уроке 28 «Списки и память».

Пример 12. Границы применимости: IndexError и поведение среза

Одиночный индекс за пределами списка — ошибка выполнения. Срез с теми же «неправильными» границами ошибку не бросает, а просто обрезается по факту.

📄 examples/index_error_demo.py
short_list = [10, 20, 30]

try:
    print(short_list[5])
except IndexError as error:
    print("IndexError:", error)
# IndexError: list index out of range

print(short_list[1:10])   # [20, 30] — срез не кидает ошибку

Что происходит: у списка из 3 элементов допустимые индексы — 0, 1, 2 (и -1…-3). Обращение к [5] валится с IndexError. А срез [1:10] ведёт себя мягче: Python просто останавливается на реальном конце списка, поэтому такая же «ошибка границ» в срезе не ломает программу.

Что делать дальше

Разберите файлы репозитория — там те же идеи в исходном виде занятия, затем переходите к заданиям. Полный разбор методов — в уроке 26, list comprehensions — в уроке 30. Если что-то из примеров не запустилось — сначала загляните в типичные ошибки.