Списки (list) в Python — полный гайд для аналитика
Коротко
list — основная структура данных в Python. Упорядоченная, изменяемая коллекция элементов любого типа. Если вы работаете с данными в Python — вы работаете со списками. Они лежат в основе pandas Series, передаются в функции, возвращаются из запросов. На собеседовании аналитика list спрашивают чаще любой другой структуры.
Создание списка
# Литерал — квадратные скобки
numbers = [1, 2, 3, 4, 5]
mixed = [1, 'два', 3.0, True]
empty = []
# Конструктор list()
from_range = list(range(10)) # [0, 1, 2, ..., 9]
from_string = list('hello') # ['h', 'e', 'l', 'l', 'o']
from_tuple = list((1, 2, 3)) # [1, 2, 3]
# List comprehension
squares = [x ** 2 for x in range(6)] # [0, 1, 4, 9, 16, 25]List comprehension — отдельная большая тема. Подробный разбор с фильтрацией, вложенными циклами и примерами для аналитиков — в статье про list comprehension.
Доступ к элементам
fruits = ['яблоко', 'банан', 'вишня', 'груша', 'слива']
fruits[0] # 'яблоко' — первый элемент
fruits[-1] # 'слива' — последний элемент
fruits[-2] # 'груша' — предпоследний
# Проверка наличия
'банан' in fruits # True
'манго' in fruits # FalseИндексация с нуля. Отрицательные индексы считают с конца: -1 — последний, -2 — предпоследний. Обращение по несуществующему индексу — IndexError.
Срезы (slicing)
Срезы — одна из самых мощных возможностей Python. Синтаксис: list[start:stop:step].
nums = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
nums[2:5] # [2, 3, 4] — с 2-го по 4-й (stop не включён)
nums[:3] # [0, 1, 2] — первые три
nums[7:] # [7, 8, 9] — с 7-го до конца
nums[::2] # [0, 2, 4, 6, 8] — каждый второй
nums[::-1] # [9, 8, 7, ..., 0] — разворот списка
# Срез создаёт новый список (shallow copy)
first_three = nums[:3]
first_three[0] = 99
print(nums[0]) # 0 — оригинал не изменилсяЗапомните: stop не включается в результат. nums[2:5] — элементы с индексами 2, 3, 4. Это согласуется с range(2, 5).
Основные методы
Добавление элементов
colors = ['красный', 'синий']
colors.append('зелёный') # ['красный', 'синий', 'зелёный']
colors.insert(1, 'белый') # ['красный', 'белый', 'синий', 'зелёный']
colors.extend(['чёрный', 'серый']) # добавляет несколько элементовappend добавляет один элемент в конец. extend добавляет все элементы из итерируемого объекта. insert(i, x) вставляет элемент на позицию i.
Частая ошибка: colors.append([1, 2]) добавит список как один элемент. Если нужно добавить элементы — используйте extend.
Удаление элементов
items = ['a', 'b', 'c', 'd', 'e']
items.remove('c') # удаляет первое вхождение 'c'
last = items.pop() # удаляет и возвращает последний элемент
second = items.pop(1) # удаляет и возвращает элемент по индексу
items.clear() # очищает списокremove выбросит ValueError, если элемента нет. pop без аргумента — O(1), pop(i) из середины — O(n).
Поиск и подсчёт
data = [3, 1, 4, 1, 5, 9, 2, 6, 5]
data.index(5) # 4 — индекс первого вхождения
data.count(1) # 2 — количество вхождений
len(data) # 9 — длина спискаСортировка и разворот
nums = [3, 1, 4, 1, 5, 9]
# In-place — меняет сам список, возвращает None
nums.sort() # [1, 1, 3, 4, 5, 9]
nums.sort(reverse=True) # [9, 5, 4, 3, 1, 1]
nums.reverse() # разворачивает список
# sorted() — возвращает новый список, оригинал не меняется
original = [3, 1, 4, 1, 5]
new_sorted = sorted(original) # [1, 1, 3, 4, 5]
print(original) # [3, 1, 4, 1, 5] — не изменился
# Сортировка по ключу
users = [('Анна', 25), ('Борис', 30), ('Вика', 22)]
users.sort(key=lambda x: x[1]) # по возрастуsort vs sorted — классический вопрос на собеседовании. sort() меняет список на месте и возвращает None. sorted() создаёт новый список. Если написать result = nums.sort(), в result будет None.
Вложенные списки
Списки могут содержать другие списки — это матрицы, таблицы, деревья.
matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
]
matrix[1][2] # 6 — вторая строка, третий столбец
# Плоский список из вложенного
flat = [x for row in matrix for x in row]
# [1, 2, 3, 4, 5, 6, 7, 8, 9]В работе аналитика вложенные списки встречаются при парсинге JSON-ответов API и при конвертации в DataFrame:
import pandas as pd
rows = [['Москва', 100], ['Питер', 80], ['Казань', 60]]
df = pd.DataFrame(rows, columns=['city', 'orders'])Копирование списков
original = [1, [2, 3], 4]
# Shallow copy — копирует ссылки, не объекты
shallow = original.copy() # или original[:] или list(original)
shallow[1].append(99)
print(original) # [1, [2, 3, 99], 4] — вложенный список изменился!
# Deep copy — копирует всё рекурсивно
import copy
deep = copy.deepcopy(original)
deep[1].append(100)
print(original) # [1, [2, 3, 99], 4] — оригинал не затронутShallow copy копирует только верхний уровень. Вложенные объекты остаются общими. Deep copy рекурсивно копирует всё дерево. Для плоских списков (числа, строки) разницы нет — shallow copy достаточно.
list vs tuple
Если list можно менять, то tuple — нельзя. tuple быстрее, экономнее по памяти и может быть ключом словаря. Используйте list, когда коллекция будет расти или меняться. tuple — когда структура фиксирована (координаты, RGB, возврат из функции). Подробное сравнение — в отдельной статье.
Частые ошибки
Изменение списка во время итерации
# Неправильно — пропускает элементы
nums = [1, 2, 3, 4, 5]
for n in nums:
if n % 2 == 0:
nums.remove(n)
print(nums) # [1, 3, 5]? Нет — [1, 3, 5] только если повезёт
# Правильно — создайте новый список
nums = [1, 2, 3, 4, 5]
odds = [n for n in nums if n % 2 != 0] # [1, 3, 5]При удалении из списка во время итерации индексы сдвигаются, и Python пропускает элементы. Решение — list comprehension или итерация по копии.
Присвоение вместо копирования
a = [1, 2, 3]
b = a # b — ссылка на тот же объект
b.append(4)
print(a) # [1, 2, 3, 4] — a тоже изменился!
b = a.copy() # теперь b — независимая копияsort() возвращает None
# Неправильно
result = [3, 1, 2].sort() # result = None
# Правильно
result = sorted([3, 1, 2]) # result = [1, 2, 3]Практические примеры для аналитика
# Список уникальных городов из данных
cities = list(set(row['city'] for row in data))
# Фильтрация и трансформация
revenues = [order['amount'] for order in orders if order['status'] == 'paid']
total = sum(revenues)
# Разбиение списка на чанки (для батчевых запросов)
def chunks(lst, n):
for i in range(0, len(lst), n):
yield lst[i:i + n]
user_ids = list(range(1000))
for batch in chunks(user_ids, 100):
process(batch) # обрабатываем по 100 за разБольше примеров и задач — на странице примеры вопросов. Разбор всех типов данных Python — в отдельном гайде.
Вопросы с собеседований
— Чем list отличается от tuple? — list мутабельный, tuple — нет. Из этого следует: tuple хешируемый, занимает меньше памяти, создаётся быстрее. list используют, когда коллекция меняется, tuple — для фиксированных структур.
— Что вернёт sort()?
— None. Метод sort() меняет список на месте и ничего не возвращает. Для получения нового отсортированного списка используйте sorted().
— Чем append отличается от extend?
— append(x) добавляет один элемент. extend(iterable) добавляет все элементы из итерируемого объекта. [1,2].append([3,4]) даст [1, 2, [3, 4]], а [1,2].extend([3,4]) даст [1, 2, 3, 4].
— Как развернуть список?
— Три способа: lst.reverse() (in-place), lst[::-1] (новый список), list(reversed(lst)) (новый список через итератор).
— Что такое shallow copy и чем она опасна?
— Shallow copy копирует только ссылки на вложенные объекты. Если внутри списка есть другие списки, изменение вложенного списка в копии затронет оригинал. Для полной независимости — copy.deepcopy().
FAQ
Когда использовать list, а когда set?
list сохраняет порядок и допускает дубликаты. set — неупорядоченный, без дубликатов, зато проверка in за O(1) вместо O(n). Если нужно проверять принадлежность — set. Если важен порядок или возможны повторы — list.
Насколько быстр append?
append работает за амортизированное O(1) — Python заранее выделяет память с запасом (overallocation). Вставка в начало insert(0, x) — O(n), потому что сдвигает все элементы. Если нужно часто добавлять в начало — используйте collections.deque.
Можно ли хранить разные типы в одном списке?
Технически — да: [1, 'два', 3.0, True]. На практике это антипаттерн. В аналитике списки обычно содержат элементы одного типа: список ID, список метрик, список строк. Разнородные данные лучше хранить в tuple или dict.
Как выбрать между list comprehension и цикла for?
List comprehension быстрее и компактнее для простых трансформаций: [x*2 for x in data]. Если логика сложная (несколько условий, побочные эффекты, обработка ошибок) — обычный цикл читаемее. Подробнее — в гайде по list comprehension.
Потренируйтесь
Знать теорию — полдела. Чтобы на собеседовании не зависать над вопросом про sort vs sorted, нужно решать задачи. В тренажёре Карьерник — вопросы по спискам, срезам и методам с разборами. 10 минут в день в Telegram — откройте тренажёр.