Перейти к содержимому
davidka.net > 💻 🧠 Код 1001 > 📚 Учебные Материалы > 🐍 Python > 📘 Коллекции в Python

📘 Коллекции в Python

Коллекции обычно поддерживают:

  • итерацию (for item in collection)
  • проверку вхождения (x in collection)
  • определение длины (len(collection))
  • доступ по индексу или ключу (если упорядочены или ассоциативны)

💡 В Python нет строгого интерфейса «коллекция», но есть неформальные протоколы. Если объект поддерживает __iter__, __len__, __contains__ — его можно считать коллекцией.


In Questo Articolo
  1. Что не является коллекцией
  2. Основные встроенные коллекции
  3. Расширенные коллекции из стандартной библиотеки
  4. Другие коллекции и коллекционоподобные типы
  5. 1. Списки — list
  6. 2. Словари — dict
  7. 3. Кортежи — tuple
  8. 4. SimpleNamespace
  9. 5. Множества — set
  10. 6. Неизменяемые множества — frozenset
  11. 7. namedtuple — именованные кортежи
  12. 8. deque — двусторонняя очередь
  13. 9. Counter — счётчик элементов
  14. 10. defaultdict — словарь со значениями по умолчанию
  15. 11. dataclass — классы данных
  16. 12. UserList — кастомные списки
  17. 13. UserDict — кастомные словари
  18. 📈 Сравнение коллекций по памяти и скорости
  19. 🧠 Общие рекомендации по производительности
  20. 📊 Сравнение коллекций
  21. 💡 Когда что использовать?

Что не является коллекцией

Следующие типы не считаются коллекциями, так как не содержат группы элементов:

  • int, float, bool — скалярные значения
  • None — отсутствие значения
  • функции, модули, классы — это объекты, но не контейнеры данных (если только не содержат __dict__)

Основные встроенные коллекции

Доступны без импортов:

ТипОписание
listУпорядоченный, изменяемый список.
tupleУпорядоченный, неизменяемый кортеж.
dictУпорядоченный (с Python 3.7+) словарь ключ-значение.
setНеупорядоченное множество уникальных элементов.
frozensetНеизменяемая версия set.

Расширенные коллекции из стандартной библиотеки

ТипМодульЗачем нужен
SimpleNamespacetypesОбъект с динамическими атрибутами (альтернатива словарю с доступом через точку).
namedtuplecollectionsНеизменяемый кортеж с именованными полями.
dequecollectionsДвусторонняя очередь — эффективна для операций на концах.
CountercollectionsСловарь-счётчик частот элементов.
defaultdictcollectionsСловарь со значениями по умолчанию.
dataclassdataclassesАвтоматически генерирует классы данных с __init__, __repr__, __eq__ и др.
UserListcollectionsБазовый класс для создания кастомных списков.
UserDictcollectionsБазовый класс для создания кастомных словарей.

Другие коллекции и коллекционоподобные типы

Хотя не всегда называются «коллекциями» в бытовом смысле, эти типы тоже хранят или представляют группы данных.

1. str — строка

Неизменяемая упорядоченная коллекция символов.

s = "Python"
print(len(s))        # → 6
print(s[0])          # → P
print('y' in s)      # → True
print(list(s))       # → ['P', 'y', 't', 'h', 'o', 'n']

2. bytes, bytearray

  • bytes — неизменяемая последовательность байтов.
  • bytearray — изменяемая версия.
b = b"hello"
print(b[0])          # → 104
print(len(b))        # → 5

ba = bytearray(b"hello")
ba[0] = 72
print(ba)            # → bytearray(b'Hello')

3. range

Ленивая упорядоченная последовательность чисел. Не хранит элементы в памяти.

r = range(3)
print(list(r))       # → [0, 1, 2]
print(1 in r)        # → True
print(r[2])          # → 2

4. array.array

Хранит однотипные числовые данные компактно (как в C).

from array import array
arr = array('i', [1, 2, 3])  # 'i' = signed int
print(arr)                   # → array('i', [1, 2, 3])

5. Генераторы и итераторы

Не хранят данные — генерируют по запросу. Не поддерживают len() или индексацию.

gen = (x * 2 for x in range(3))
print(list(gen))     # → [0, 2, 4]
# len(gen) → TypeError

6. ChainMap (из collections)

Объединяет несколько словарей в одну виртуальную коллекцию — поиск идёт по цепочке.

from collections import ChainMap

d1 = {'a': 1}
d2 = {'b': 2}
cm = ChainMap(d1, d2)
print(cm['a'])       # → 1
print(cm['b'])       # → 2

7. OrderedDict (из collections)

Словарь с гарантированным порядком вставки. Актуален для Python < 3.7.

from collections import OrderedDict

od = OrderedDict([('a', 1), ('b', 2)])
print(od)            # → OrderedDict([('a', 1), ('b', 2)])

8. enum.Enum, enum.Flag

Коллекции именованных констант

from enum import Enum
class Color(Enum):
    RED = 1
    GREEN = 2
print(list(Color))   # → [, ]

9. typing.NamedTuple, typing.TypedDict

Типизированные обёртки над namedtuple и dict.

from typing import NamedTuple, TypedDict

class Person(NamedTuple):
    name: str
    age: int

p = Person("Алиса", 25)

class Movie(TypedDict):
    title: str
    year: int

m: Movie = {"title": "Matrix", "year": 1999}

10. heapq, bisect — инструменты, а не коллекции

Работают с коллекциями, но сами коллекциями не являются:

  • heapq — поддержка кучи через списки.
  • bisect — вставка в отсортированный список с сохранением порядка.

1. Списки — list

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

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

Создание: []

boris_list = ["Борис", "Москва", 30, "инженер"]
print(f"Создание списка: {boris_list}")
# → Создание списка: ['Борис', 'Москва', 30, 'инженер']

print(f"Элемент по индексу 0: {boris_list[0]}")
# → Элемент по индексу 0: Борис

boris_list[2] = 31
print(f"Изменение элемента: {boris_list}")
# → Изменение элемента: ['Борис', 'Москва', 31, 'инженер']

boris_list.append("женат")
print(f"Добавление в конец: {boris_list}")
# → Добавление в конец: ['Борис', 'Москва', 31, 'инженер', 'женат']

boris_list.insert(1, "Россия")
print(f"Вставка элемента: {boris_list}")
# → Вставка элемента: ['Борис', 'Россия', 'Москва', 31, 'инженер', 'женат']

boris_list.remove("инженер")
print(f"Удаление по значению: {boris_list}")
# → Удаление по значению: ['Борис', 'Россия', 'Москва', 31, 'женат']

del boris_list[2]
print(f"Удаление по индексу: {boris_list}")
# → Удаление по индексу: ['Борис', 'Россия', 31, 'женат']

boris_list.extend(["хобби", "рыбалка"])
print(f"Расширение списка: {boris_list}")
# → Расширение списка: ['Борис', 'Россия', 31, 'женат', 'хобби', 'рыбалка']

boris_list.pop()
print(f"Удаление с конца: {boris_list}")
# → Удаление с конца: ['Борис', 'Россия', 31, 'женат', 'хобби']

2. Словари — dict

Коллекция пар ключ → значение. Ключи должны быть хешируемыми. С Python 3.7 сохраняет порядок вставки.

Полезен для структурированных данных: профили, конфиги, JSON.

Создание: {}

alice_dict = {"name": "Алиса", "age": 25, "city": "Лондон", "occupation": "художница"}
print(f"Создание словаря: {alice_dict}")
# → Создание словаря: {'name': 'Алиса', 'age': 25, 'city': 'Лондон', 'occupation': 'художница'}

print(f"Значение по ключу 'name': {alice_dict['name']}")
# → Значение по ключу 'name': Алиса

alice_dict["age"] = 26
print(f"Изменение значения: {alice_dict}")
# → Изменение значения: {'name': 'Алиса', 'age': 26, 'city': 'Лондон', 'occupation': 'художница'}

alice_dict["hobby"] = "рисование"
print(f"Добавление пары: {alice_dict}")
# → Добавление пары: {'name': 'Алиса', 'age': 26, 'city': 'Лондон', 'occupation': 'художница', 'hobby': 'рисование'}

del alice_dict["city"]
print(f"Удаление пары: {alice_dict}")
# → Удаление пары: {'name': 'Алиса', 'age': 26, 'occupation': 'художница', 'hobby': 'рисование'}

hobby = alice_dict.pop("hobby")
print(f"Удаление с возвратом: {alice_dict}, значение: {hobby}")
# → Удаление с возвратом: {'name': 'Алиса', 'age': 26, 'occupation': 'художница'}, значение: рисование

print(f"Есть ли ключ 'name': {'name' in alice_dict}")
# → Есть ли ключ 'name': True

3. Кортежи — tuple

Упорядоченная, неизменяемая коллекция. Подходит для фиксированных данных.

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

Создание: ()

boris_tuple = ("Борис", "Москва", 30, "инженер")
print(f"Создание кортежа: {boris_tuple}")
# → Создание кортежа: ('Борис', 'Москва', 30, 'инженер')

print(f"Элемент по индексу 2: {boris_tuple[2]}")
# → Элемент по индексу 2: 30

# boris_tuple[0] = "Иван"  → TypeError
# boris_tuple.append("что-то") → AttributeError

💡 Кортежи занимают меньше памяти и работают быстрее списков. Идеальны, когда изменяемость не нужна.


4. SimpleNamespace

Простой класс из модуля types для создания объектов с динамическими атрибутами. Доступ — через точку (obj.attr).

Полезен, когда нужен синтаксис obj.name без создания класса.

from types import SimpleNamespace

alice_ns = SimpleNamespace(name="Алиса", age=25, city="Лондон")
print(f"Объект: {alice_ns}")
# → Объект: namespace(name='Алиса', age=25, city='Лондон')

print(f"Имя: {alice_ns.name}")
# → Имя: Алиса

alice_ns.age = 26
print(f"После изменения: {alice_ns}")
# → После изменения: namespace(name='Алиса', age=26, city='Лондон')

alice_ns.occupation = "художница"
print(f"С новым атрибутом: {alice_ns}")
# → С новым атрибутом: namespace(name='Алиса', age=26, city='Лондон', occupation='художница')

del alice_ns.city
print(f"После удаления: {alice_ns}")
# → После удаления: namespace(name='Алиса', age=26, occupation='художница')

setattr(alice_ns, "hobby", "рисование")
print(f"Через setattr: {alice_ns}")
# → Через setattr: namespace(name='Алиса', age=26, occupation='художница', hobby='рисование')

delattr(alice_ns, "hobby")
print(f"Через delattr: {alice_ns}")
# → Через delattr: namespace(name='Алиса', age=26, occupation='художница')

💡 Альтернатива словарю, когда удобнее писать obj.name, а не obj['name'].


5. Множества — set

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

Применяется для удаления дубликатов и проверки вхождения.

Создание: {} или set()

numbers = {1, 2, 3, 3, 2, 1}
print(f"Множество: {numbers}")
# → Множество: {1, 2, 3}

numbers.add(4)
print(f"После добавления: {numbers}")
# → После добавления: {1, 2, 3, 4}

numbers.remove(2)
print(f"После удаления: {numbers}")
# → После удаления: {1, 3, 4}

other = {3, 4, 5}
print(f"Объединение: {numbers | other}")
# → Объединение: {1, 3, 4, 5}

print(f"Пересечение: {numbers & other}")
# → Пересечение: {3, 4}

print(f"Разность: {numbers - other}")
# → Разность: {1}

6. Неизменяемые множества — frozenset

Неизменяемая версия set. Можно использовать как ключ в словаре или элемент другого множества.

frozen = frozenset([1, 2, 3, 2])
print(f"frozenset: {frozen}")
# → frozenset: frozenset({1, 2, 3})

other = frozenset([3, 4])
print(f"Пересечение: {frozen & other}")
# → Пересечение: frozenset({3})

print(f"Объединение: {frozen | other}")
# → Объединение: frozenset({1, 2, 3, 4})

# frozen.add(5) → AttributeError

7. namedtuple — именованные кортежи

Неизменяемая структура с доступом по именам полей. Аналог кортежа, но читаемее.

from collections import namedtuple

Person = namedtuple("Person", ["name", "age", "city"])
alice = Person("Алиса", 25, "Лондон")

print(f"Объект: {alice}")
# → Объект: Person(name='Алиса', age=25, city='Лондон')

print(f"Имя: {alice.name}")
# → Имя: Алиса

print(f"Возраст: {alice[1]}")
# → Возраст: 25

# alice.age = 26 → AttributeError (неизменяемый)

alice_new = alice._replace(age=26)
print(f"Копия с изменением: {alice_new}")
# → Копия с изменением: Person(name='Алиса', age=26, city='Лондон')

💡 Отлично подходит для представления записей: точки, пользователи, конфиги — когда важна неизменяемость и читаемость.


8. deque — двусторонняя очередь

Оптимизирован для быстрых операций в начале и конце. Аналог списка, но эффективнее для appendleft, popleft.

from collections import deque

d = deque([1, 2, 3])
print(f"Исходный deque: {d}")
# → Исходный deque: deque([1, 2, 3])

d.appendleft(0)
print(f"Добавление слева: {d}")
# → Добавление слева: deque([0, 1, 2, 3])

d.append(4)
print(f"Добавление справа: {d}")
# → Добавление справа: deque([0, 1, 2, 3, 4])

left = d.popleft()
print(f"Удаление слева: {left}, осталось: {d}")
# → Удаление слева: 0, осталось: deque([1, 2, 3, 4])

right = d.pop()
print(f"Удаление справа: {right}, осталось: {d}")
# → Удаление справа: 4, осталось: deque([1, 2, 3])

💡 Используется в алгоритмах: BFS, кэширование (LRU), буферы, где важна производительность на концах.


9. Counter — счётчик элементов

Подсчитывает частоту элементов в итерируемом объекте. Удобен для статистики, анализа.

from collections import Counter

text = "абракадабра"
c = Counter(text)
print(f"Счётчик букв: {c}")
# → Счётчик букв: Counter({'а': 5, 'б': 2, 'р': 2, 'к': 1, 'д': 1})

print(f"Частота 'а': {c['а']}")
# → Частота 'а': 5

print(f"Топ-3: {c.most_common(3)}")
# → Топ-3: [('а', 5), ('б', 2), ('р', 2)]

c2 = Counter("буква")
c.update(c2)
print(f"После update: {c}")
# → После update: Counter({'а': 6, 'б': 3, 'р': 2, 'к': 2, 'д': 1, 'у': 1, 'в': 1})

💡 Полезен для анализа текстов, логов, голосований — везде, где нужно посчитать, «что встречается чаще».


10. defaultdict — словарь со значениями по умолчанию

Автоматически создаёт значение по умолчанию при отсутствии ключа. Убирает необходимость проверок if key in dict.

from collections import defaultdict

dd_list = defaultdict(list)
dd_list["fruits"].append("яблоко")
dd_list["fruits"].append("банан")
print(f"Список: {dict(dd_list)}")
# → Список: {'fruits': ['яблоко', 'банан']}

dd_int = defaultdict(int)
for char in "абракадабра":
    dd_int[char] += 1
print(f"Счётчик: {dict(dd_int)}")
# → Счётчик: {'а': 5, 'б': 2, 'р': 2, 'к': 1, 'д': 1}

dd_set = defaultdict(set)
dd_set["cities"].add("Москва")
dd_set["cities"].add("СПб")
print(f"Множество: {dict(dd_set)}")
# → Множество: {'cities': {'Москва', 'СПб'}}

💡 Убирает шаблонный код вида if key not in d: d[key] = []. Делает код чище и безопаснее.


11. dataclass — классы данных

Декоратор для автоматического создания классов с инициализатором, строковым представлением, сравнением и т.д.

from dataclasses import dataclass

@dataclass
class Person:
    name: str
    age: int
    city: str = "Не указан"

alice = Person("Алиса", 25)
print(f"Объект: {alice}")
# → Объект: Person(name='Алиса', age=25, city='Не указан')

print(f"Имя: {alice.name}")
# → Имя: Алиса

alice.age = 26
print(f"После изменения: {alice}")
# → После изменения: Person(name='Алиса', age=26, city='Не указан')

bob = Person("Боб", 30)
print(f"Алиса == Боб: {alice == bob}")
# → Алиса == Боб: False

@dataclass(frozen=True)
class ImmutablePerson:
    name: str
    age: int

ivan = ImmutablePerson("Иван", 40)
# ivan.age = 41 → FrozenInstanceError

💡 Заменяет ручное написание __init__, __repr__, __eq__. Идеален для DTO, конфигов, моделей.


12. UserList — кастомные списки

Наследуется от collections.UserList. Используется, чтобы создать список с переопределённым поведением.

from collections import UserList

class LoggingList(UserList):
    def append(self, item):
        print(f"[LOG] Добавление: {item}")
        super().append(item)

    def remove(self, item):
        print(f"[LOG] Удаление: {item}")
        super().remove(item)

log_list = LoggingList([1, 2, 3])
print(f"Исходный: {log_list}")
# → Исходный: [1, 2, 3]

log_list.append(4)
# → [LOG] Добавление: 4
print(f"После append: {log_list}")
# → После append: [1, 2, 3, 4]

log_list.remove(2)
# → [LOG] Удаление: 2
print(f"После remove: {log_list}")
# → После remove: [1, 3, 4]

💡 Полезен, когда нужно добавить логирование, валидацию, или изменить поведение стандартного списка.


13. UserDict — кастомные словари

Наследуется от collections.UserDict. Используется, чтобы создать словарь с переопределённым поведением.

from collections import UserDict

class LowerKeyDict(UserDict):
    def __setitem__(self, key, value):
        key = key.lower() if isinstance(key, str) else key
        super().__setitem__(key, value)

    def __getitem__(self, key):
        key = key.lower() if isinstance(key, str) else key
        return super().__getitem__(key)

ld = LowerKeyDict()
ld["Name"] = "Алиса"
print(f"Значение по 'Name': {ld['Name']}")
# → Значение по 'Name': Алиса
print(f"Значение по 'name': {ld['name']}")
# → Значение по 'name': Алиса
print(f"Ключи: {list(ld.keys())}")
# → Ключи: ['name']

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


📈 Сравнение коллекций по памяти и скорости

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


1. Память: list vs tuple vs array.array

Создадим коллекции из 1 000 000 целых чисел и сравним размер в памяти.

import sys
from array import array

n = 1_000_000
data = list(range(n))        # list
data_t = tuple(range(n))     # tuple
data_a = array('i', range(n)) # array.array

print(f"list:  {sys.getsizeof(data) / 1024 / 1024:.2f} MB")
# → list:  8.00 MB (примерно)

print(f"tuple: {sys.getsizeof(data_t) / 1024 / 1024:.2f} MB")
# → tuple: 8.00 MB (почти как list)

print(f"array: {sys.getsizeof(data_a) / 1024 / 1024:.2f} MB")
# → array: 3.81 MB

💡 Вывод:

  • array.array экономит ~2x памяти для чисел.
  • list и tuple потребляют примерно одинаково, но tuple немного быстрее при итерации.

2. Скорость доступа: list vs tuple vs array.array

Замерим время доступа к каждому элементу в коллекции из 10 000 000 элементов.

import time

def time_access(collection, name):
    start = time.perf_counter()
    total = 0
    for i in range(len(collection)):
        total += collection[i]
    end = time.perf_counter()
    print(f"{name}: {end - start:.4f} секунд")

n = 10_000_000
lst = list(range(n))
tpl = tuple(range(n))
arr = array('i', range(n))

time_access(lst, "list")
# → list: 1.2000 секунд (примерно)

time_access(tpl, "tuple")
# → tuple: 1.0000 секунд (быстрее list)

time_access(arr, "array")
# → array: 0.8000 секунд (самый быстрый)

💡 Вывод:

  • array.array — самый быстрый для чисел.
  • tuple быстрее list на 10–20%.
  • Разница заметна на больших объёмах.

3. Память: dict vs SimpleNamespace vs dataclass

Создадим объекты с 5 полями и сравним размер.

from types import SimpleNamespace
from dataclasses import dataclass

@dataclass
class PersonDC:
    name: str
    age: int
    city: str
    hobby: str
    job: str

# Создаём экземпляры
d = {"name": "A", "age": 25, "city": "X", "hobby": "Y", "job": "Z"}
ns = SimpleNamespace(name="A", age=25, city="X", hobby="Y", job="Z")
dc = PersonDC("A", 25, "X", "Y", "Z")

print(f"dict:      {sys.getsizeof(d)} байт")
# → dict:      232 байт

print(f"SimpleNamespace: {sys.getsizeof(ns)} байт")
# → SimpleNamespace: 64 байт (только служебные данные; атрибуты хранятся в __dict__)

print(f"dataclass: {sys.getsizeof(dc)} байт")
# → dataclass: 64 байт (аналогично)

# Но учитываем __dict__:
print(f"ns.__dict__: {sys.getsizeof(ns.__dict__)} байт")
# → ns.__dict__: 232 байт

print(f"dc.__dict__: {sys.getsizeof(dc.__dict__)} байт")
# → dc.__dict__: 232 байт

💡 Вывод:

  • SimpleNamespace и dataclass потребляют столько же памяти, сколько dict, потому что используют __dict__.
  • Если нужна экономия — используйте __slots__ (см. ниже).

4. Экономия памяти: dataclass с __slots__

@dataclass
class PersonSlots:
    __slots__ = ("name", "age", "city", "hobby", "job")
    name: str
    age: int
    city: str
    hobby: str
    job: str

dc_slots = PersonSlots("A", 25, "X", "Y", "Z")
print(f"dataclass + slots: {sys.getsizeof(dc_slots)} байт")
# → dataclass + slots: 80 байт (без __dict__)

# Попытка добавить новый атрибут:
# dc_slots.new = "value" → AttributeError

💡 Вывод:

  • __slots__ экономит память и ускоряет доступ к атрибутам.
  • Цена — нельзя добавлять новые атрибуты динамически.

5. Скорость поиска: list vs set

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

import time

n = 1_000_000
lst = list(range(n))
st = set(range(n))

def time_in(collection, target, name):
    start = time.perf_counter()
    for _ in range(1000):
        _ = target in collection
    end = time.perf_counter()
    print(f"{name} (поиск {target}): {end - start:.4f} секунд")

time_in(lst, 999_999, "list")
# → list (поиск 999999): 10.0000 секунд (линейный поиск)

time_in(st, 999_999, "set")
# → set (поиск 999999): 0.0005 секунд (хеш-таблица)

💡 Вывод:

  • set в тысячи раз быстрее list для проверки вхождения.
  • Всегда используй set, если нужно часто проверять x in collection.

6. Память: set vs frozenset

s = set(range(1000))
fs = frozenset(range(1000))

print(f"set:       {sys.getsizeof(s)} байт")
# → set:       32792 байт

print(f"frozenset: {sys.getsizeof(fs)} байт")
# → frozenset: 32792 байт

💡 Вывод:

  • frozenset и set потребляют одинаково.
  • Разница только в изменяемости.

7. Скорость добавления: list.append vs deque.append vs deque.appendleft

from collections import deque
import time

def time_append(collection, n, method='append'):
    start = time.perf_counter()
    for i in range(n):
        if method == 'appendleft' and hasattr(collection, 'appendleft'):
            collection.appendleft(i)
        else:
            collection.append(i)
    end = time.perf_counter()
    return end - start

n = 100_000

lst = []
dq = deque()

time_list_append = time_append(lst, n)
time_deque_append = time_append(dq, n)
time_deque_appendleft = time_append(deque(), n, 'appendleft')

print(f"list.append:      {time_list_append:.4f} сек")
# → list.append:      0.0100 сек

print(f"deque.append:     {time_deque_append:.4f} сек")
# → deque.append:     0.0100 сек

print(f"deque.appendleft: {time_deque_appendleft:.4f} сек")
# → deque.appendleft: 0.0100 сек

# А теперь попробуем list.insert(0, x) — очень медленно!
lst = []
start = time.perf_counter()
for i in range(n):
    lst.insert(0, i)  # O(n) на каждую вставку!
end = time.perf_counter()
print(f"list.insert(0):   {end - start:.4f} сек")
# → list.insert(0):   5.0000 сек (квадратичное время!)

💡 Вывод:

  • deque.appendleft работает за O(1), в отличие от list.insert(0), который O(n).
  • Для частых операций на обоих концах — только deque.

🧠 Общие рекомендации по производительности

СитуацияЧто выбратьПочему
Хранение чисел, важна памятьarray.arrayВ 2x меньше памяти, быстрее доступ
Данные не меняютсяtupleБыстрее list, безопаснее
Частая проверка x in collectionset / frozensetO(1) против O(n) у list
Операции на концахdequeappendleft/popleft за O(1)
Структурированные данные, память критичнаdataclass + __slots__Без __dict__, меньше памяти
Подсчёт частотCounterОптимизирован под эту задачу
Кастомное поведениеUserList / UserDictБезопасное расширение стандартных коллекций

📊 Сравнение коллекций

ТипУпорядоченИзменяемУникальные элементыДоступ по индексуДубликаты
list✅ Да✅ Да❌ Нет✅ Да✅ Да
tuple✅ Да❌ Нет❌ Нет✅ Да✅ Да
dict✅ Да*✅ ДаКлючи — да❌ НетЗначения — да
set❌ Нет✅ Да✅ Да❌ Нет❌ Нет
frozenset❌ Нет❌ Нет✅ Да❌ Нет❌ Нет
SimpleNamespace✅ Да (атрибуты)✅ Да❌ Нет (атрибуты могут повторяться)❌ Нет✅ Да
namedtuple✅ Да❌ Нет❌ Нет✅ Да✅ Да
deque✅ Да✅ Да❌ Нет✅ Да✅ Да
Counter❌ Нет✅ Да❌ Нет❌ Нет (но есть ключи)✅ Да
defaultdict✅ Да*✅ ДаКлючи — да❌ НетЗначения — да
dataclass✅ Да (поля)✅ Да (если не frozen)❌ Нет❌ Нет✅ Да
UserList✅ Да✅ Да❌ Нет✅ Да✅ Да
UserDict✅ Да*✅ ДаКлючи — да❌ НетЗначения — да
str✅ Да❌ Нет❌ Нет✅ Да✅ Да
bytes✅ Да❌ Нет❌ Нет✅ Да✅ Да
bytearray✅ Да✅ Да❌ Нет✅ Да✅ Да
range✅ Да❌ Нет❌ Нет✅ Да❌ Нет
array.array✅ Да✅ Да❌ Нет✅ Да✅ Да
ChainMap✅ Да*✅ ДаКлючи — да❌ НетЗначения — да
Enum✅ Да❌ Нет✅ Да (элементы)❌ Нет❌ Нет
  • — с Python 3.7+ словари, defaultdict, UserDict, ChainMap сохраняют порядок вставки.

💡 Когда что использовать?

ЗадачаКоллекция
Хранить последовательность с изменениямиlist / deque / UserList / bytearray
Зафиксировать данные, защитить от измененийtuple / namedtuple / frozenset / str / bytes
Быстрый поиск, удаление дубликатовset / frozenset
Хранить структурированные данныеdict / dataclass / SimpleNamespace / UserDict / TypedDict
Использовать множество как ключfrozenset
Временная структура с доступом через точкуSimpleNamespace / dataclass
Подсчёт частотCounter
Автоматическое создание значений по умолчаниюdefaultdict
Эффективные операции на концахdeque
Кастомное поведение спискаUserList
Кастомное поведение словаряUserDict
Работа с бинарными даннымиbytes / bytearray / array.array
Конфиги с иерархиейChainMap
Именованные константыEnum
Ленивые последовательностиrange / генераторы

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *