Списки в Python: хранение в памяти, управление и освобождение

0
15

Содержание

Краткая памятка по работе со списками в Python

  1. Список — это динамический массив указателей на объекты в куче.
  2. При создании списка Python выделяет память с запасом (over-allocation).
  3. Память управляется через арены (256 КБ), пулы (4 КБ) и блоки.
  4. Счетчик ссылок немедленно освобождает память при удалении последней ссылки.
  5. Сборщик мусора (GC) обрабатывает циклические ссылки.
  6. Используйте sys.getsizeof() для оценки занимаемой памяти.
  7. Для однотипных данных предпочитайте array.array или numpy для экономии памяти.
  8. Избегайте частых вставок/удалений в середине списка — используйте collections.deque.
  9. Копируйте списки через list.copy() или срез [:] для создания независимой копии.
  10. Проверяйте наличие элемента через оператор in (работает за O(n)).
  11. Для поиска индекса используйте list.index() с обработкой исключения ValueError.
  12. Генераторы списков (list comprehensions) быстрее и читаемее циклов for.
  13. Срезы (slicing) создают новый список, не изменяя исходный.
  14. Удаляйте элементы через pop() (с конца — O(1), с начала — O(n)).
  15. Для сортировки используйте list.sort() (in-place) или sorted() (новый список).

Основы списков в Python: структура и применение

Обучение - изображение номер один
Обучение — изображение номер один

Структурно список представляет собой последовательность элементов, каждый из которых имеет свой индекс. Нумерация индексов начинается с 0, что типично для большинства языков программирования.

Однажды я столкнулся с задачей обработки большого массива данных о покупках в интернет-магазине. Клиент хотел понимать, какие товары часто покупают вместе. На первый взгляд, задача казалась сложной — десятки тысяч заказов, каждый с множеством товаров.

Решение пришло, когда я осознал мощь вложенных списков Python. Каждый заказ представлял собой список товаров, а все заказы образовывали список списков. Это позволило мне использовать всего несколько строк кода для анализа:

# Пример данных о заказах orders = [[«хлеб», «масло», «молоко»], [«молоко», «яйца», «сахар»], [«хлеб», «молоко», «яйца»]] # Находим товары, которые часто покупают вместе pairs = {} for order in orders: for item1 in order: for item2 in order: if item1!= item2: pair = tuple(sorted([item1, item2])) pairs[pair] = (pair, 0) + 1 # Выводим результаты for pair, count in sorted((), key=lambda x: x[1], reverse=True): print(f»{pair[0]} и {pair[1]} купили вместе {count} раз»)

Благодаря гибкости списков, задача была решена менее чем за час, а клиент получил ценную аналитику для оптимизации рекомендаций.

  • Упорядоченность — элементы хранятся в определенном порядке
  • Изменяемость — можно добавлять, удалять и изменять элементы
  • Индексация — доступ к элементам по индексу (начиная с 0)
  • Гетерогенность — могут содержать элементы разных типов
  • Вложенность — списки могут содержать другие списки

Таблица №1

Область применения Пример использования Преимущество списков
Обработка данных Хранение и анализ временных рядов Динамическое добавление новых измерений
Веб-разработка Работа с JSON-данными Естественное преобразование в JSON-массивы
Машинное обучение Хранение признаков объектов Удобная векторизация данных
Алгоритмы Реализация стека или очереди Встроенные методы для добавления/удаления
GUI-приложения Управление коллекциями элементов Простота обновления интерфейса

Понимание фундаментальных принципов работы списков даёт вам мощный инструмент для структурирования и манипулирования данными в Python. С этой основой давайте перейдем к более практическим аспектам. 💡

Python и структуры данных (списки), введение в програмирование - изображение номер два
Python и структуры данных (списки), введение в програмирование — изображение номер два

Lists - изображение номер три
Lists — изображение номер три

Что такое список (list) в Python?

Презентация \ - изображение номер четыре
Презентация \ — изображение номер четыре

Список (list)
— тип данных, предназначенный для хранения набора или последовательности разных элементов.

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

Объявление списка

5 листингов для лучшего понимания - изображение номер пять
5 листингов для лучшего понимания — изображение номер пять

Python - изображение номер шесть
Python — изображение номер шесть

Объявление списка — самый первый и главный этап его создания. Для объявления списка в Python существует несколько способов.

В данном примере мы создали список с заранее известными данными. Если нужен пустой список, в квадратных скобках ничего не указывается — elements = [].

Обращение к элементу списка в Python

Python как универсальный инструмент реализации количественных исследований - пре - изображение номер семь
Python как универсальный инструмент реализации количественных исследований — пре — изображение номер семь

Язык программирования - изображение номер восемь
Язык программирования — изображение номер восемь

Отрицательные индексы работают справа налево (то есть индекс значения ‘1’ — -4, а отрицательный индекс ‘word’ — -1.

💡 Отрицательным индексом удобно пользоваться, когда необходимо обратиться к последнему элементу в списке, не высчитывая его номер. Любой конечный элемент будет с индексом, равным -1.

Создание и наполнение списков: способы и синтаксис

Основы программирования на - изображение номер девять
Основы программирования на — изображение номер девять

Python предлагает несколько элегантных способов создания и наполнения списков. Выбор конкретного метода зависит от контекста вашей задачи и стиля программирования.

# С помощью квадратных скобок empty_list = [] # С помощью конструктора list() empty_list_constructor = list()

# С явным указанием элементов fruits = [«яблоко», «банан», «апельсин»] # Через конструктор list из другой итерируемой структуры fruits_from_string = list(«абвг») # [‘а’, ‘б’, ‘в’, ‘г’] fruits_from_tuple = list((«яблоко», «банан», «апельсин»))

# Простой генератор списка squares = [x**2 for x in range(10)] # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] # С условием even_squares = [x**2 for x in range(10) if x % 2 == 0] # [0, 4, 16, 36, 64] # Вложенные генераторы matrix = [[i*j for j in range(3)] for i in range(3)] # [[0, 0, 0], [0, 1, 2], [0, 2, 4]]

Генераторы списков — одна из самых мощных возможностей Python. Они позволяют создавать списки одной элегантной строкой кода, избегая громоздких циклов for.

При наполнении списков важно помнить о нескольких особенностях и возможных подводных камнях:

Таблица №2

Особенность Описание Пример
Ссылочная семантика При копировании списка создаётся ссылка, а не новый объект

Для эффективной работы с данными важно выбирать подходящий способ создания списка. Например, если вам нужно преобразовать данные, генератор списков обычно работает быстрее и требует меньше кода, чем цикл for с append(). 🔄

# Сравнение подходов к созданию списка # Менее эффективный подход squares_traditional = [] for i in range(1000): squares_traditional.append(i**2) # Более эффективный подход squares_comprehension = [i**2 for i in range(1000)]

Выбор способа создания списка существенно влияет на читаемость и производительность вашего кода. Теперь, когда мы знаем, как создавать списки, давайте рассмотрим, как их модифицировать.

Добавление в список

Списки в - изображение номер десять
Списки в — изображение номер десять

Массивы в - изображение номер одиннадцать
Массивы в — изображение номер одиннадцать

В списках доступно добавление, изменение, удаление элементов. Рассмотрим каждый способ изменения элементов на примерах.

Для того чтобы добавить новый элемент в список, используется (x), где list — список, x — нужное значение.

Добавление в список на указанную позицию

Python 3 - изображение номер двенадцать
Python 3 — изображение номер двенадцать

Программирование на языке - изображение номер тринадцать
Программирование на языке — изображение номер тринадцать

Немаловажно обратить внимание на метод (i, x), где list — список, i — позиция, x — нужное значение.

Изменение элементов списка

Введение в программирование на - изображение номер четырнадцать
Введение в программирование на — изображение номер четырнадцать

Списки (list) в - изображение номер пятнадцать
Списки (list) в — изображение номер пятнадцать

Изменение элементов списка происходит следующим образом: нужно выбрать элемент по индексу (порядковому номеру элемента) и присвоить новое значение.

Не забывайте, что счёт начинается с нуля, и в данном списке цифра 6 это 2-й элемент

Удаление элемента из списка

4 способа удалить элемент из списка - изображение номер шестнадцать
4 способа удалить элемент из списка — изображение номер шестнадцать

#3300 post - изображение номер семнадцать
#3300 post — изображение номер семнадцать

Для удаление из списка используют инструкцию del list[i], где list — список, i — индекс (позиция) элемента в списке:

Еще один способ удаления из списка — (x), где list — список, x — значение, которое нужно удалить:

Как проверить наличие элемента в списке

Проверка наличия элемента в списке в - изображение номер восемнадцать
Проверка наличия элемента в списке в — изображение номер восемнадцать

Python program to check a list is empty or not - изображение номер девятнадцать
Python program to check a list is empty or not — изображение номер девятнадцать

Для того чтобы проверить существование какого-либо элемента в списке, нужно воспользоваться оператором in. Рассмотрим на примере:

Копирование списка Python

How to copy a list in python - изображение номер двадцать
How to copy a list in python — изображение номер двадцать

How to - изображение номер двадцать один
How to — изображение номер двадцать один

Если вы захотите скопировать список оператором =, вы скопируете не сам список, а только его ссылку.

  • () — встроенный метод copy (доступен с Python 3.3);
  • list(elements) — через встроенную функцию list();
  • (elements) — функция copy() из пакета copy;
  • elements[:] — через создание среза (устаревший синтаксис).

Важно
: (a) делает поверхностное копирование. Объекты внутри списка будут скопированы как ссылки на них (как в случае с оператором =). Если необходимо рекурсивно копировать всех элементов в списке, используйте (a)

Скопировать часть списка можно с помощью срезов. Есть несколько вариантов использования:

Цикл по списку

Циклы в - изображение номер двадцать два
Циклы в — изображение номер двадцать два

Списки (массивы) - изображение номер двадцать три
Списки (массивы) — изображение номер двадцать три

Попробуем построить цикл while. Он выполняется, когда есть какое-либо определённое условие:

Из примеров выше можем сделать вывод, что конструкция for выглядит заметно компактнее, чем while.

Как списки хранятся в памяти?

Задать список python - изображение номер двадцать четыре
Задать список python — изображение номер двадцать четыре

Базовая C-структура списков в Python (CPython) выглядит следующим образом:

Когда мы создаём список, в памяти под него резервируется объект, состоящий из 3-х частей:

  • PyObject_VAR_HEAD — заголовок;
  • ob_item — массив указателей на элементы списка;
  • allocated — количество выделенной памяти под элементы списка.

Python размещает элементы списка в памяти, затем размещает указатели на эти элементы. Таким образом, список в Python — это массив указателей.

Кортежи, словари, списки в - изображение номер двадцать пять
Кортежи, словари, списки в — изображение номер двадцать пять

Диспетчер памяти: «командовать парадом буду я»

Глава 17 - изображение номер двадцать шесть
Глава 17 — изображение номер двадцать шесть

Python — это интерпретируемый язык программирования, поэтому перед запуском программы код на языке Python компилируется в машиночитаемые инструкции — байт-код. Инструкции байт-кода интерпретируются виртуальной машиной, определяемой реализацией языка, например, стандартной — CPython.

В начале выполнения программы операционная система создает новый процесс и выделяет под него ресурсы. Выделенную виртуальную память интерпретатор использует для 1) собственной корректной работы, 2) стека вызываемых функций и их аргументов и 3) хранилища данных, представленного в виде кучи.
Оговоримся, что CPython не взаимодействует напрямую с регистрами и ячейками физической памяти — только с ее виртуальным представлением.

В отличие от C/C++, мы не можем управлять состоянием кучи напрямую из Python. Функции низкоуровневой работы с памятью предоставляются Python/C API, но обычно интерпретатор просто обращается к хранилищу данных через диспетчер памяти Python (memory manager).

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

GIL гарантирует: в один и тот же момент времени байт-код выполняется только одним потоком. Главное преимущество — безопасная работа с памятью, а основной недостаток в том, что многопоточное выполнение программ Python требует специфических решений.
Фактически за это отвечает даже не диспетчер задач, который ожидает гостей за регистрационной стойкой, а GIL — глобальная блокировка интерпретатора.

Очевидно, программа не сама выполняет сохранение и освобождение памяти — ведь мы не пишем соответствующих инструкций. Интерпретатор лишь запрашивает сделать это. А диспетчер уже делегирует работу, связанную с хранением данных, аллокаторам — распределителям памяти.
диспетчер памяти

Организация доступной виртуальной памяти

Python stores the list into txt, and then reads it as list - изображение номер двадцать семь
Python stores the list into txt, and then reads it as list — изображение номер двадцать семь

Непосредственно с оперативной памятью взаимодействует распределитель сырой памяти (raw memory allocator). Поверх него работают аллокаторы, реализующие стратегии управления памятью, специфичные для отдельных типов объектов. Объекты разных типов — например, числа и строки — занимают разный объем, к ним применяются разные механизмы хранения и освобождения памяти. Аллокаторы стараются не занимать лишнюю память до тех пор, пока она не станет совершенно необходимой — этот момент определен стратегией распределения памяти CPython.

ЧИТАТЬ ТАКЖЕ:  Как подключиться к базе данных MySQL на Python: PyMySQL, установка и выполнение запросов

Python использует динамическую стратегию, то есть распределение памяти выполняется во время выполнения программы. Виртуальная память Python представляет иерархическую структуру, оптимизированную под объекты Python размером менее 256 Кб:

  • Арена — фрагмент памяти, расположенный в пределах непрерывного блока оперативной памяти объемом 256 Кб. Объекты размером более 256 Кб направляются в стандартный аллокатор C.
  • Пул — блок памяти внутри арены, занимающий 4 Кб, что соответствует одной странице виртуальной памяти. То есть одна арена включает до 256/4 = 64 пулов.
  • Блок — элемент пула размером от 16 до 512 байт. В пределах пула все блоки имеют одинаковый размер. Размер блока определяется тем, сколько байт требуется для представления конкретного объекта. Размеры блоков кратны 16 байт. То есть существует всего 512/16 = 32 классов (size class) блоков. То есть в одном пуле, в зависимости от класса, может находиться от 8 до 256 блоков.

LIST data structures in - изображение номер двадцать восемь
LIST data structures in — изображение номер двадцать восемь

Блок

Python память переменная - изображение номер двадцать девять
Python память переменная — изображение номер двадцать девять
  1. untouched — блок еще не использовался для хранения данных;
  2. free — блок использовался механизмом памяти, но больше не содержит используемых программой данных;
  3. allocated — блок хранит данные, необходимые для выполнения программы.

В пределах пула блоки free организованы в односвязный список с указателем freeblock. Если аллокатору для выделения памяти не хватит блоков списка freeblock, он задействует блоки untouched. Освобождение памяти означает всего лишь то, что аллокатор меняет статус блока с allocated на free и начинает отслеживать блок в списке freeblock.

Пул

Object - изображение номер тридцать
Object — изображение номер тридцать

Пул может находиться в одном из трех состояний: used (занят), full (заполнен), empty (пуст). Пустые пулы отличаются от занятых отсутствием блоков allocated и тем, что для них пока не определен size class. Пулы full полностью заполнены блоками allocated и недоступны для записи. Стоит освободиться любому из блоков заполненного пула — и он помечается как used.

Пулы одного типа и одного размера блоков организованы в двусвязные списки. Это позволяет алгоритму легко находить доступное пространство для блока заданного размера. Алгоритм проверяет список usedpools и размещает блок в доступном пуле. Если в usedpools нет ни одного подходящего пула для запроса, алгоритм использует пул из списка freepools, который отслеживает пулы в состоянии empty.

Арена

Как работает память в - изображение номер тридцать один
Как работает память в — изображение номер тридцать один

Информацию о текущем распределении памяти в аренах, пулах и блоках можно посмотреть, запустив функцию sys._debugmallocstats():

Чтобы не произошло утечки памяти, диспетчер памяти должен отследить, что вся выделенная память освободится после завершения работы программы. То есть при завершении программы CPython дает задание очистить все арены.

Именно количество используемых арен определяет объем оперативной памяти, занимаемой программой на Python — если в арене все пулы в состоянии empty, СPython делает запрос на освобождение этого участка виртуальной памяти. Но уже понятно: чтобы пулы стали empty, все их блоки должны быть free или untouched. Получается, нужно понять, как CPython освобождает память.

Освобождение памяти: счетчик ссылок, сборщик мусора

Устройство - изображение номер тридцать два
Устройство — изображение номер тридцать два

Для освобождения памяти используются два механизма: счетчик ссылок и сборщик мусора.

Всё в Python является объектами, а прародителем всех типов объектов в реализации CPython является PyObject. От него наследуются все остальные типы. В PyObject определены счетчик ссылок и указатель на фактический тип объекта. Счетчик ссылок увеличивается на единицу, когда мы создаем что-то, что обращается к объекту, например, сохраняем объект в новой переменной. И наоборот, счетчик уменьшается на единицу, когда мы перестаем ссылаться на объект.

Счетчик ссылок любого объекта можно проверить с помощью (). Учтите, что передача объекта в getrefcount() увеличивает счетчик ссылок на 1, так как сам вызов метода создает еще одну ссылку. Когда счетчик уменьшается до нуля, происходит вызов аллокатора для освобождения соответствующих блоков памяти.

Однако счетчик ссылок неспособен отследить ситуации с циклическими ссылками. К примеру, возможна ситуация, когда два объекта ссылаются друг на друга, но оба уже не используются программой. Для борьбы с такими зависимостями используется сборщик мусора (garbage collector).

Если счетчик ссылок является свойством объекта, то сборщик мусора — механизм, который запускается на основе эвристик. Задача этих эвристик — снизить частоту и объем очищаемых данных. Основная стратегия заключается в разделении объектов на поколения: чем больше сборок мусора пережил объект, тем он значимее для выполнения работы программы. Сборщик мусора имеет интерфейс в виде модуля gc.

Создание, изменение, удаление списков и работа с его элементами

Списки - изображение номер тридцать три
Списки — изображение номер тридцать три

Если у вас уже есть список и вы хотите создать его копию, то можно воспользоваться следующим способом:

В случае, если вы выполните простое присвоение списков друг другу, то переменной b будет присвоена ссылка на тот же элемент данных в памяти, на который ссылается a, а не копия списка а. Т.е. если вы будете изменять список a, то и b тоже будет меняться.

Для удаления элемента из списка, в случае, если вы знаете его значение, используйте метод remove(x), при этом будет удалена первая ссылка на данный элемент.

Очистить список можно просто заново его проинициализировав, так как будто вы его вновь создаете. Для получения доступа к элементу списка укажите индекс этого элемента в квадратных скобках.

Можно использовать отрицательные индексы, в таком случае счет будет идти с конца, например для доступа к последнему элементу списка можно использовать вот такую команду:

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

Добавление и удаление элементов в списке Python

Mastering - изображение номер тридцать четыре
Mastering — изображение номер тридцать четыре

Одна из ключевых особенностей списков в Python — их изменяемость. Это позволяет динамически добавлять, удалять и заменять элементы по мере выполнения программы. Рассмотрим основные методы манипуляции элементами списка.

  • append() — добавляет элемент в конец списка
  • insert() — вставляет элемент по указанному индексу
  • extend() — добавляет все элементы из другого итерируемого объекта
  • + (оператор сложения) — создаёт новый список, объединяя два существующих

# Примеры добавления элементов fruits = [«яблоко», «груша»] # append() – добавляет один элемент в конец («банан») # [‘яблоко’, ‘груша’, ‘банан’] # insert() – вставляет элемент по индексу (1, «апельсин») # [‘яблоко’, ‘апельсин’, ‘груша’, ‘банан’] # extend() – добавляет элементы из итерируемого объекта ([«киви», «манго»]) # [‘яблоко’, ‘апельсин’, ‘груша’, ‘банан’, ‘киви’, ‘манго’] # + оператор создает новый список new_fruits = fruits + [«ананас», «дыня»]

  • append() добавляет аргумент как один элемент, даже если это список
  • extend() добавляет каждый элемент из итерируемого объекта отдельно
  • remove() — удаляет первый элемент с указанным значением
  • pop() — удаляет и возвращает элемент по индексу (по умолчанию последний)
  • del — оператор для удаления элемента по индексу или среза
  • clear() — удаляет все элементы из списка

fruits = [‘яблоко’, ‘апельсин’, ‘груша’, ‘банан’, ‘апельсин’] # remove() – удаляет первое вхождение значения (‘апельсин’) # [‘яблоко’, ‘груша’, ‘банан’, ‘апельсин’] # pop() – удаляет и возвращает элемент по индексу last_fruit = () # ‘апельсин’ first_fruit = (0) # ‘яблоко’ # fruits теперь [‘груша’, ‘банан’] # del – удаляет элемент или срез fruits = [‘яблоко’, ‘апельсин’, ‘груша’, ‘банан’] del fruits[1] # [‘яблоко’, ‘груша’, ‘банан’] del fruits[1:3] # [‘яблоко’] # clear() – удаляет все элементы () # []

На одном из моих тренингов по Python для аналитиков данных участники столкнулись с проблемой, которая отлично иллюстрирует особенности удаления элементов из списка.

Задача была такой: удалить из списка все отрицательные числа. Один из участников написал следующий код:

Но код работал неправильно — некоторые отрицательные числа оставались в списке. Проблема в том, что при удалении элемента все последующие элементы смещаются влево, а индекс i продолжал увеличиваться. Таким образом, некоторые элементы пропускались.

# Способ 1: Итерация от конца к началу numbers = [5, -2, 7, -8, 10, -4, 3] for i in range(len(numbers) – 1, -1, -1): if numbers[i] < 0: del numbers[i] # Способ 2: Создание нового списка (более «питонический») numbers = [5, -2, 7, -8, 10, -4, 3] numbers = [x for x in numbers if x >= 0]

Этот случай помог участникам понять, как важно учитывать изменение структуры списка при его модификации в процессе итерации.

Важное замечание: удаление элементов из списка в процессе итерации может привести к непредсказуемым результатам. Существует несколько безопасных способов:

  • Создать новый список с фильтром (list comprehension)
  • Использовать функцию filter()
  • Итерироваться по списку в обратном порядке
  • Использовать копию списка для итерации

# Безопасные способы удаления элементов при итерации numbers = [1, 2, 3, 4, 5, 6] # Способ 1: List comprehension even_numbers = [x for x in numbers if x % 2 == 0] # [2, 4, 6] # Способ 2: filter() even_numbers = list(filter(lambda x: x % 2 == 0, numbers)) # [2, 4, 6] # Способ 3: Обратная итерация for i in range(len(numbers) – 1, -1, -1): if numbers[i] % 2!= 0: del numbers[i] # numbers будет [2, 4, 6] # Способ 4: Итерация по копии for x in (): if x % 2!= 0: (x)

Понимание тонкостей добавления и удаления элементов в списках позволяет избегать распространённых ошибок и писать более эффективный код. Следующий шаг — освоение методов сортировки и изменения порядка элементов. 🧩

Сортировка и изменение порядка элементов списка

Python program to get a list ,sorted in increasing order by the last element eac - изображение номер тридцать пять
Python program to get a list ,sorted in increasing order by the last element eac — изображение номер тридцать пять

Сортировка данных — одна из наиболее частых операций при обработке информации. Python предоставляет мощные и гибкие инструменты для сортировки и изменения порядка элементов в списках.

  • sort() — сортирует список на месте (изменяет оригинальный список)
  • sorted() — возвращает новый отсортированный список, не изменяя оригинал
  • reverse() — обращает порядок элементов на месте
  • reversed() — возвращает итератор с элементами в обратном порядке

# Базовые примеры сортировки numbers = [3, 1, 4, 1, 5, 9, 2, 6] # sort() – сортирует на месте () print(numbers) # [1, 1, 2, 3, 4, 5, 6, 9] # sorted() – возвращает новый список original = [3, 1, 4, 1, 5, 9] sorted_numbers = sorted(original) print(original) # [3, 1, 4, 1, 5, 9] – не изменился print(sorted_numbers) # [1, 1, 3, 4, 5, 9] # Сортировка в обратном порядке (reverse=True) print(numbers) # [9, 6, 5, 4, 3, 2, 1, 1]

# Сортировка по длине строк words = [«яблоко», «груша», «банан», «киви»] (key=len) print(words) # [‘киви’, ‘груша’, ‘яблоко’, ‘банан’] # Сортировка словарей по значению определенного ключа students = [{«name»: «Анна», «grade»: 4.5}, {«name»: «Иван», «grade»: 3.8}, {«name»: «Мария», «grade»: 4.9}] sorted_students = sorted(students, key=lambda x: x[«grade»], reverse=True) # [{‘name’: ‘Мария’, ‘grade’: 4.9}, {‘name’: ‘Анна’, ‘grade’: 4.5}, {‘name’: ‘Иван’, ‘grade’: 3.8}]

# Обращение списка fruits = [«яблоко», «груша», «банан»] # reverse() – меняет порядок на месте () print(fruits) # [‘банан’, ‘груша’, ‘яблоко’] # reversed() – возвращает итератор original = [«яблоко», «груша», «банан»] reversed_fruits = list(reversed(original)) print(original) # [‘яблоко’, ‘груша’, ‘банан’] – не изменился print(reversed_fruits) # [‘банан’, ‘груша’, ‘яблоко’]

Python позволяет реализовать многоуровневую сортировку, когда элементы сравниваются по нескольким критериям:

# Сортировка по нескольким критериям employees = [(«Иванов», «Иван», 30), («Петров», «Петр», 25), («Иванов», «Алексей», 35), («Сидоров», «Иван», 30)] # Сортировка по фамилии, затем по имени, затем по возрасту sorted_employees = sorted(employees, key=lambda x: (x[0], x[1], x[2])) # [(‘Иванов’, ‘Алексей’, 35), (‘Иванов’, ‘Иван’, 30), (‘Петров’, ‘Петр’, 25), (‘Сидоров’, ‘Иван’, 30)] # Сортировка по фамилии (по возрастанию), затем по возрасту (по убыванию) from operator import itemgetter sorted_employees = sorted(employees, key=itemgetter(0)) # По фамилии sorted_employees = sorted(sorted_employees, key=itemgetter(2), reverse=True) # По возрасту (по убыванию)

ЧИТАТЬ ТАКЖЕ:  Как в Python перевести число из десятичной системы в шестнадцатеричную

Для сложных объектов можно использовать модуль operator и его функции itemgetter и attrgetter:

from operator import itemgetter, attrgetter # Для списка кортежей sorted(employees, key=itemgetter(0, 1)) # Сортировка по первым двум элементам # Для списка объектов с атрибутами class Person: def __init__(self, name, age): = name = age people = [Person(«Алиса», 25), Person(«Боб», 20), Person(«Чарли», 30)] sorted_people = sorted(people, key=attrgetter(«age»))

Таблица №3

Метод Изменяет исходный список Возвращает результат Поддержка key и reverse Типичное использование
sort() Да None Да Когда оригинальный порядок не нужен
sorted() Нет Новый список Да Когда нужно сохранить оригинальный список
reverse() Да None Нет Для простого обращения порядка элементов
reversed() Нет Итератор Нет Для экономии памяти при обращении порядка

Эффективная сортировка списков — важный навык, который позволяет быстро организовывать и анализировать данные. Особенно это актуально при работе с большими объемами информации, где правильная сортировка может значительно ускорить последующие операции поиска и фильтрации. 📊

Срезы и итерация: мощные методы работы со списками

Что такое срезы в - изображение номер тридцать шесть
Что такое срезы в — изображение номер тридцать шесть

Срезы (slices) и итерация — это инструменты, которые поднимают работу со списками на новый уровень, позволяя элегантно извлекать подмножества элементов и обрабатывать данные. Правильное использование этих техник часто отличает код новичка от кода опытного Python-разработчика.

  • start — индекс начала среза (включительно)
  • stop — индекс конца среза (не включительно)
  • step — шаг (по умолчанию 1)

numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] # Базовые срезы first_three = numbers[0:3] # [0, 1, 2] middle = numbers[3:7] # [3, 4, 5, 6] # Сокращенный синтаксис from_beginning = numbers[:5] # [0, 1, 2, 3, 4] to_end = numbers[5:] # [5, 6, 7, 8, 9] full_copy = numbers[:] # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] # Отрицательные индексы (отсчет с конца) last_three = numbers[-3:] # [7, 8, 9] except_last_two = numbers[:-2] # [0, 1, 2, 3, 4, 5, 6, 7] # Шаг every_second = numbers[::2] # [0, 2, 4, 6, 8] every_third_reverse = numbers[::-3] # [9, 6, 3, 0]

numbers = [0, 1, 2, 3, 4, 5] # Замена среза numbers[1:4] = [10, 20, 30] # [0, 10, 20, 30, 4, 5] # Замена среза на последовательность другой длины numbers[1:4] = [100, 200] # [0, 100, 200, 4, 5] # Удаление среза numbers[1:3] = [] # [0, 4, 5] # Вставка без замены numbers[1:1] = [42, 43] # [0, 42, 43, 4, 5]

fruits = [«яблоко», «груша», «банан», «апельсин»] # Простая итерация по элементам for fruit in fruits: print(fruit) # Итерация с индексами for i, fruit in enumerate(fruits): print(f»Индекс {i}: {fruit}») # Параллельная итерация по нескольким спискам prices = [100, 150, 80, 120] for fruit, price in zip(fruits, prices): print(f»{fruit} стоит {price} рублей») # Итерация с условной фильтрацией for fruit in fruits: if len(fruit) > 5: print(f»{fruit} — длинное название»)

Особая сила Python проявляется при использовании итерации в сочетании с другими конструкциями языка:

# Функциональные инструменты работы со списками numbers = [1, 2, 3, 4, 5] # map() — применяет функцию к каждому элементу squares = list(map(lambda x: x**2, numbers)) # [1, 4, 9, 16, 25] # filter() — отбирает элементы по условию even = list(filter(lambda x: x % 2 == 0, numbers)) # [2, 4] # reduce() — последовательно применяет функцию к парам элементов from functools import reduce sum_all = reduce(lambda x, y: x + y, numbers) # 15 # all() и any() — проверка условий all_positive = all(x > 0 for x in numbers) # True any_even = any(x % 2 == 0 for x in numbers) # True

Таблица №4

Подход Преимущества Недостатки Пример использования
Цикл for Простой, понятный, гибкий Более многословный Сложные преобразования с условиями
List comprehension Краткий, элегантный, читаемый Ограничен простыми операциями Трансформация всех элементов списка
map()/filter() Функциональный стиль Менее читабелен для новичков Работа с функциями высшего порядка
NumPy/Pandas операции Очень быстрые, векторизованные Требуют дополнительных библиотек Научные и аналитические вычисления

Срезы и итерации особенно эффективны при работе с большими данными. Важно отметить, что срезы создают копии данных, что может быть затратно по памяти для очень больших списков. В таких случаях иногда лучше использовать генераторы и итераторы для потоковой обработки. 🔄

# Пример использования генераторного выражения вместо списка large_list = list(range(1000000)) # Потребляет много памяти large_squares = [x**2 for x in large_list] # Более эффективно по памяти (ленивые вычисления) large_squares_gen = (x**2 for x in large_list) for square in large_squares_gen: # обработка каждого значения по одному pass

Овладение техниками срезов и итерации — ключевой навык для эффективного программирования на Python. Эти инструменты позволяют писать чистый, краткий и производительный код для обработки списков любой сложности. 🚀

Изучение списков в Python — фундаментальный шаг для каждого разработчика. Мы рассмотрели основные операции: создание, наполнение, модификацию, сортировку и итерацию. Понимание особенностей каждого метода и выбор правильного инструмента для конкретной задачи значительно повышает качество и эффективность кода. Когда вы встречаете сложную задачу обработки данных, стоит сначала подумать: «Как я могу элегантно решить это с помощью списков?» Скорее всего, Python уже предлагает готовый метод или комбинацию методов для вашей конкретной задачи. Не останавливайтесь на базовом использовании — экспериментируйте с продвинутыми техниками, которые превратят ваш код из функционального в действительно впечатляющий.

Методы списков

Майк - изображение номер тридцать семь
Майк — изображение номер тридцать семь

Добавляет элемент в конец списка. Ту же операцию можно сделать так a[len(a):] = [x].

Расширяет существующий список за счет добавления всех элементов из списка L. Эквивалентно команде a[len(a):] = L.

Вставить элемент x в позицию i. Первый аргумент – индекс элемента после которого будет вставлен элемент x.

Удаляет элемент из позиции i и возвращает его. Если использовать метод без аргумента, то будет удален последний элемент из списка.

Сортирует элементы в списке по возрастанию. Для сортировки в обратном порядке используйте флаг reverse=True. Дополнительные возможности открывает параметр key, за более подробной информацией обратитесь к документации.

List Comprehensions

Генераторы списков в - изображение номер тридцать восемь
Генераторы списков в — изображение номер тридцать восемь

List Comprehensions чаще всего на русский язык переводят как абстракция списков или списковое включение, является частью синтаксиса языка, которая предоставляет простой способ построения списков. Проще всего работу list comprehensions показать на примере. Допустим вам необходимо создать список целых чисел от 0 до n, где n предварительно задается. Классический способ решения данной задачи выглядел бы так:

Использование list comprehensions позволяет сделать это значительно проще:

Слайсы / Срезы

Python: коллекции, часть 2/4: индексирование, срезы, сортировка / - изображение номер тридцать девять
Python: коллекции, часть 2/4: индексирование, срезы, сортировка / — изображение номер тридцать девять

Слайсы (срезы) являются очень мощной составляющей Python, которая позволяет быстро и лаконично решать задачи выборки элементов из списка. Выше уже был пример использования слайсов, здесь разберем более подробно работу с ними. Создадим список для экспериментов:

Слайс задается тройкой чисел, разделенных запятой: start:stop:step. Start – позиция с которой нужно начать выборку, stop – конечная позиция, step – шаг. При этом необходимо помнить, что выборка не включает элемент определяемый stop.

>>> # Получить копию списка >>> a[:] [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] >>> # Получить первые пять элементов списка >>> a[0:5] [0, 1, 2, 3, 4] >>> # Получить элементы с 3-го по 7-ой >>> a[2:7] [2, 3, 4, 5, 6] >>> # Взять из списка элементы с шагом 2 >>> a[::2] [0, 2, 4, 6, 8] >>> # Взять из списка элементы со 2-го по 8-ой с шагом 2 >>> a[1:8:2] [1, 3, 5, 7]

Вложенные списки

Лекции - изображение номер сорок
Лекции — изображение номер сорок

Для обращения к элементу вложенного списка нужно использовать два индекса: первый указывает на индекс главного списка, второй — индекс элемента во вложенном списке. Вот пример:

Генераторы списков

#28 - изображение номер сорок один
#28 — изображение номер сорок один

Генератором списка называется способ построения списка с применением выражения к каждому элементу, входящему в последовательность. Есть схожесть генератора списка и цикла for. На этом примере мы рассмотрим простейший генератор списков:

Таким образом мы получили отдельно взятые утроенные буквы слова, введённого в кавычки. Есть множество вариантов применения генератора списков.

Это усложнённая конструкция генератора списков, в которой мы сделали все возможные наборы сочетаний букв из введённых слов. Буквы-исключения видны по циклу, где стоит знак!= для одной переменной и другой.

Best Practices

Best - изображение номер сорок два
Best — изображение номер сорок два

Последние абзацы статьи будут посвящены лучшим решениям практических задач, с которыми так или иначе сталкивается Python-разработчик.

Как перевести список в другой формат?

29 - изображение номер сорок три
29 — изображение номер сорок три

Иногда требуется перевести список в строку, в словарь или в JSON. Для этого нужно будет вывести список без скобок.

Перевод списка в строку осуществляется с помощью функции join(). На примере это выглядит так:

Словарь в Python — это такая же встроенная структура данных, наряду со списком. Преобразование списка в словарь — задача тоже несложная. Для этого потребуется воспользоваться функцией dict(). Вот пример преобразования:

JSON — это JavaScript Object Notation. В Python находится встроенный модуль json для кодирования и декодирования данных JSON. С применением метода (x) можно запросто преобразовать список в строку JSON.

Как узнать индекс элемента в списке?

5 способов найти индекс элемента в списке - изображение номер сорок четыре
5 способов найти индекс элемента в списке — изображение номер сорок четыре

Узнать позицию элемента в последовательности списка бывает необходимым, когда элементов много, вручную их не сосчитать, и нужно обращение по индексу. Для того чтобы узнать индекс элемента, используют функцию (x).

В качестве аргумента передаем значение, а на выходе получаем его индекс.

Как посчитать количество уникальных элементов в списке?

Посчитать количество элементов в списке - изображение номер сорок пять
Посчитать количество элементов в списке — изображение номер сорок пять

Самый простой способ — приведение списка к set (множеству). После этого останутся только уникальные элементы, которые мы посчитаем функцией len():

Как создать список числовых элементов с шагом

Создание списка числовых элементов с шагом может понадобиться не так и часто, но мы рассмотрим пример построения такого списка.

Шагом называется переход от одного элемента к другому. Если шаг отрицательный, произойдёт реверс массива, то есть отсчёт пойдёт справа налево. Вот так выглядит список с шагом.

При разработке на языке Python, списки встречаются довольно часто. Знание основ работы со списками поможет быстро и качественно писать программный код 😉.

Часто задаваемые вопросы о хранении списков в Python

Вопрос: Как Python управляет памятью при создании списка?
Ответ: Python выделяет блок памяти под массив указателей на объекты, а при необходимости расширяет его, выделяя дополнительное место (over-allocation) для оптимизации последующих добавлений.

Вопрос: Что такое арена, пул и блок в контексте памяти Python?
Ответ: Это уровни организации памяти в диспетчере памяти Python: арена — большой сегмент (256 КБ), пул — часть арены (4 КБ), блок — минимальная единица для хранения объектов.

Вопрос: Почему список занимает больше памяти, чем сумма его элементов?
Ответ: Потому что список хранит не сами объекты, а ссылки на них (указатели), плюс служебные данные (длина, capacity) и зарезервированное место для будущих элементов.

Вопрос: Как работает сборщик мусора при удалении списка?
Ответ: Сборщик мусора (GC) отслеживает циклические ссылки, а счетчик ссылок немедленно освобождает память, когда на объект больше нет ссылок.

Вопрос: Влияет ли тип элементов списка на его хранение в памяти?
Ответ: Да, каждый элемент — это указатель на объект в куче, поэтому разнородные списки хранят ссылки на объекты разных типов, каждый со своим размером.

Вопрос: Что такое over-allocation в списках Python?
Ответ: Это стратегия выделения памяти с запасом (обычно на 12.5% больше) при расширении списка, чтобы избежать частых перераспределений.

Вопрос: Как проверить, сколько памяти занимает список?
Ответ: Используйте sys.getsizeof(list_object) для размера самого списка (без учета объектов-элементов) и getsizeof для каждого элемента.

Вопрос: Чем отличается хранение списка от хранения кортежа в памяти?
Ответ: Кортеж неизменяем, поэтому Python не резервирует дополнительное место (over-allocation), и он занимает меньше памяти, чем список с теми же элементами.

Вопрос: Как удаление элемента из середины списка влияет на память?
Ответ: Python сдвигает все последующие элементы на одну позицию влево, что требует времени O(n), но память не освобождается сразу — capacity остается прежним.

Вопрос: Можно ли вручную управлять памятью списка?
Ответ: Косвенно — через del для удаления ссылок, вызов gc.collect() для принудительной сборки мусора или использование array.array для однотипных данных.