← Назад к вопросам

За счет чего Python может хранить разные объекты в списке и эффективно их обрабатывать

1.2 Junior🔥 181 комментариев
#Python

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI29 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Как Python хранит разные объекты в списке и обрабатывает их эффективно

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

1. Всё в Python — объект (Everything is an Object)

Каждый объект в Python имеет три компонента:

import sys

int_obj = 42
str_obj = "hello"
list_obj = [1, 2, 3]

print(f"int: id={id(int_obj)}, type={type(int_obj)}, size={sys.getsizeof(int_obj)}")
print(f"str: id={id(str_obj)}, type={type(str_obj)}, size={sys.getsizeof(str_obj)}")
print(f"list: id={id(list_obj)}, type={type(list_obj)}, size={sys.getsizeof(list_obj)}")

# Результат:
# int: id=140735263438440, type=<class 'int'>, size=28
# str: id=140735277123680, type=<class 'str'>, size=54
# list: id=140735277294784, type=<class 'list'>, size=64

Три компонента:

  • id — уникальный идентификатор (адрес в памяти)
  • type — тип объекта (int, str, list, class, function)
  • value — значение (данные объекта)

2. Список как массив указателей

Внутри список Python хранит не сами объекты, а указатели (referencias) на них:

import ctypes

mixed_list = [42, "hello", 3.14, [1, 2], {"key": "value"}]

# Список физически содержит указатели на объекты
# [ptr_to_int, ptr_to_str, ptr_to_float, ptr_to_list, ptr_to_dict]

# Получить адреса объектов
for i, obj in enumerate(mixed_list):
    print(f"{i}: {type(obj).__name__:10} = {obj!r:20} @ {id(obj)}")

# Результат:
# 0: int        = 42                     @ 140735263438440
# 1: str        = 'hello'                @ 140735277123680
# 2: float      = 3.14                   @ 140735277294784
# 3: list       = [1, 2]                 @ 140735277294928
# 4: dict       = {'key': 'value'}       @ 140735277295072

Внутреннее представление:

mixed_list (PyListObject)
┌─────────────────────────┐
│ size = 5                │
│ allocated = 8           │
│ data pointer ──┐        │
└─────────────┼──────────┘
              │
              v
       ┌──────────────────┐
       │ [0] ──────┐      │
       │ [1] ──────┼─┐    │
       │ [2] ──────┼─┼─┐  │
       │ [3] ──────┼─┼─┼──┼─┐
       │ [4] ──────┼─┼─┼──┼─┼─┐
       │ ...       │ │ │  │ │ │
       └───────────┼─┼─┼──┼─┼─┘
                   │ │ │  │ │
                   v v v  v v
              (int) (str) (float) (list) (dict)

3. Динамическая типизация через PyObject_GetAttr

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

def add_with_type_check(obj1, obj2):
    """Python примерно так работает на уровне C"""
    # 1. Проверить тип obj1
    if isinstance(obj1, int):
        if isinstance(obj2, int):
            return obj1 + obj2  # int.__add__(obj1, obj2)
    elif isinstance(obj1, str):
        if isinstance(obj2, str):
            return obj1 + obj2  # str.__add__(obj1, obj2)
    elif isinstance(obj1, list):
        if isinstance(obj2, list):
            return obj1 + obj2  # list.__add__(obj1, obj2)
    
    raise TypeError(f"unsupported operand type(s)")

# В реальности Python использует type dispatch через C API
mixed_list = [1, "a", 3.14]
print(mixed_list[0] + 10)   # int.__add__ → 11
print(mixed_list[1] + "b")  # str.__add__ → "ab"
print(mixed_list[2] + 0.86) # float.__add__ → 4.0

4. Эффективность обработки

Полиморфизм через duck typing

# Метод работает с объектами, у которых есть метод __len__
def count_items(obj):
    return len(obj)  # Работает для list, str, dict, tuple, set...

mixed_list = [[1, 2, 3], "hello", (10, 20, 30)]

for item in mixed_list:
    print(f"{type(item).__name__}: length = {count_items(item)}")

# Результат:
# list: length = 3
# str: length = 5
# tuple: length = 3

Это duck typing — Python не проверяет тип, а проверяет наличие метода.

Кэширование методов

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

import time

# Первый вызов медленнее (поиск метода)
obj = "hello"
start = time.time()
for _ in range(1_000_000):
    obj.upper()  # Первый раз медленнее
print(f"Первый вызов: {(time.time() - start)*1000:.2f}ms")

# Повторный вызов быстрее (метод в кэше)
start = time.time()
for _ in range(1_000_000):
    obj.upper()  # Второй раз быстрее
print(f"Повторный вызов: {(time.time() - start)*1000:.2f}ms")

5. Оптимизация для NumPy и Pandas

Для efficiency Data Scientist'ы часто уходят от списков Python:

import numpy as np
import pandas as pd

# Неэффективно: смешанные типы в списке
python_list = [1, 2, 3, 4, 5]
result_list = []
for x in python_list:
    result_list.append(x * 2)  # Медленно!

# Эффективно: NumPy array (одного типа)
np_array = np.array([1, 2, 3, 4, 5], dtype=np.int32)
result_np = np_array * 2  # Быстро! (vectorized)

# Для смешанных данных: Pandas DataFrame
df = pd.DataFrame({
    'numbers': [1, 2, 3],
    'strings': ['a', 'b', 'c'],
    'floats': [1.1, 2.2, 3.3]
})
# Каждый столбец оптимизирован под свой тип

6. sys.getsizeof и управление памятью

import sys

mixed_list = [42, "hello", 3.14, [1, 2], {"key": "value"}]

print(f"Размер самого списка: {sys.getsizeof(mixed_list)} байт")
print(f"Размер всех объектов: {sum(sys.getsizeof(obj) for obj in mixed_list)} байт")
print(f"Общий размер: {sys.getsizeof(mixed_list) + sum(sys.getsizeof(obj) for obj in mixed_list)} байт")

# Результат:
# Размер самого списка: 64 байт (заголовок + массив указателей)
# Размер всех объектов: 162 байт
# Общий размер: 226 байт

Выводы

Python хранит разные объекты эффективно благодаря:

  1. Указателям (references) — список содержит адреса объектов, не сами объекты
  2. Объектной модели — каждый объект знает свой тип через __class__
  3. Полиморфизму — методы вызываются через динамическую диспетчеризацию
  4. Duck typing — проверка наличия метода, а не типа
  5. Кэшированию — повторные вызовы методов быстрые

Для максимальной эффективности при работе с большими данными нужны NumPy/Pandas — они хранят данные одного типа в компактных C-массивах, что даёт 100x+ ускорение при vectorized операциях.

За счет чего Python может хранить разные объекты в списке и эффективно их обрабатывать | PrepBro