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

Что можно померить с помощью len в Python?

2.0 Middle🔥 141 комментариев
#Python Core

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

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

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

Функция len() в Python: что можно измерить?

len() — одна из самых фундаментальных функций Python. Она возвращает длину (количество элементов) объектов. Но это работает не со всем — только с sizing объектами. Объясню подробно.

Основное: что работает с len()

# Строки
len("Hello")  # 5
len("")  # 0

# Списки
len([1, 2, 3, 4])  # 4
len([])  # 0

# Кортежи
len((1, 2, 3))  # 3
len(())  # 0

# Словари
len({"a": 1, "b": 2})  # 2 (количество ключей)
len({})  # 0

# Множества
len({1, 2, 3})  # 3
len(set())  # 0

# Байтовые последовательности
len(b"bytes")  # 5
len(b"")  # 0

Как это работает: len() magic method

# len() вызывает метод __len__() у объекта
my_list = [1, 2, 3]
len(my_list)  # Вызывает my_list.__len__()
# Оба эквивалентны:
assert len(my_list) == my_list.__len__()

# Можно создать свой класс с len()
class CustomCollection:
    def __init__(self, items):
        self.items = items
    
    def __len__(self):
        """Этот метод вызывает len()"""
        return len(self.items)

collection = CustomCollection([1, 2, 3, 4, 5])
print(len(collection))  # 5

Генераторы НЕ имеют len()

# ❌ Генераторы не поддерживают len()
def my_generator():
    for i in range(10):
        yield i

gen = my_generator()
len(gen)  # ❌ TypeError: object of type 'generator' has no len()

# Почему?
# Генератор может быть бесконечным или загружаться динамически
# Размер неизвестен заранее

infinite_gen = (x for x in iter(int, 1))  # Бесконечный генератор
# len(infinite_gen)  # ❌ Невозможно

# ✅ Если нужна длина, конвертируй в список
gen = my_generator()
len(list(gen))  # 10 (но это загружает всё в память!)

Диапазоны (range) имеют len()

# range — это "ленивая" последовательность
len(range(10))  # 10
len(range(5, 15))  # 10 (с 5 по 14 включительно)
len(range(0, 20, 2))  # 10 (0, 2, 4, 6, ...)

# Это эффективнее, чем list(range(10))
list_len = len(list(range(10)))  # Создаёт список
range_len = len(range(10))  # О(1), не создаёт список
assert list_len == range_len

Пользовательские классы: правильная реализация len()

class Stack:
    def __init__(self):
        self._items = []
    
    def push(self, item):
        self._items.append(item)
    
    def pop(self):
        if self:
            return self._items.pop()
        raise IndexError("pop from empty stack")
    
    def __len__(self):
        """Должен вернуть целое число >= 0"""
        return len(self._items)
    
    def __bool__(self):
        """Используется в условиях: if stack"""
        return len(self) > 0

stack = Stack()
stack.push(1)
stack.push(2)
print(len(stack))  # 2
print(bool(stack))  # True

stack.pop()
stack.pop()
print(len(stack))  # 0
print(bool(stack))  # False

Требования к len()

class MyClass:
    def __len__(self):
        # Должен вернуть неотрицательное целое число
        # ❌ Плохо
        return "5"  # Не целое число
        return -5    # Отрицательное число
        return 5.5   # Не целое число
        
        # ✅ Хорошо
        return 0
        return 42
        return 1_000_000

# Python проверит это и выбросит TypeError/ValueError
obj = MyClass()
len(obj)  # ❌ TypeError

Практический пример: Page-based pagination

class PaginatedList:
    def __init__(self, items, page_size=10):
        self.items = items
        self.page_size = page_size
    
    def __len__(self):
        """Количество элементов в коллекции"""
        return len(self.items)
    
    def get_page(self, page_num):
        """Получить страницу по номеру"""
        start = (page_num - 1) * self.page_size
        end = start + self.page_size
        return self.items[start:end]
    
    @property
    def total_pages(self):
        """Расчёт количества страниц"""
        return (len(self) + self.page_size - 1) // self.page_size

paginated = PaginatedList(list(range(100)), page_size=10)
print(f"Total items: {len(paginated)}")  # 100
print(f"Total pages: {paginated.total_pages}")  # 10
print(f"Page 1: {paginated.get_page(1)}")  # [0, 1, 2, ..., 9]
print(f"Page 10: {paginated.get_page(10)}")  # [90, 91, ..., 99]

Какие объекты НЕ имеют len()

# ❌ Генераторы
gen = (x for x in range(10))
len(gen)  # TypeError

# ❌ Итераторы
it = iter([1, 2, 3])
len(it)  # TypeError

# ❌ Числа
len(42)  # TypeError

# ❌ None
len(None)  # TypeError

# ❌ Boolean
len(True)  # TypeError

# ✅ Что работает: контейнеры с известным размером
# str, list, tuple, dict, set, frozenset, bytes, range, collections.deque

Производительность: O(1) vs O(n)

# len() должна быть O(1) — мгновенная операция
my_list = list(range(1_000_000))
import time

start = time.time()
result = len(my_list)
print(f"Time: {(time.time() - start) * 1e6:.2f} µs")  # ~0.5 µs

# ❌ Неправильная реализация __len__() — O(n)
class BadList:
    def __init__(self, items):
        self.items = items
    
    def __len__(self):
        # Плохо! Пересчитываем каждый раз
        count = 0
        for item in self.items:
            count += 1
        return count

bad_list = BadList(list(range(1_000_000)))
start = time.time()
result = len(bad_list)
print(f"Time: {(time.time() - start) * 1e6:.2f} µs")  # ~10000 µs (медленнее!)

# ✅ Правильная реализация — O(1)
class GoodList:
    def __init__(self, items):
        self.items = items
        self._length = len(items)  # Кешируем
    
    def __len__(self):
        return self._length

Хитрости с len()

# Проверка пустоты: len() > 0
my_list = [1, 2, 3]
if len(my_list) > 0:  # Работает
    print("Not empty")

# ✅ Лучше использовать bool
if my_list:  # Проще и быстрее
    print("Not empty")

# ❌ Неправильно
if len(my_list):  # Медленнее, чем просто if my_list
    print("Not empty")

# Почему? Потому что Python автоматически проверяет __bool__() или __len__()
# Явный вызов len() лишний

Вложенные коллекции

# len() считает элементы первого уровня
matrix = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
]

len(matrix)  # 3 (количество строк)
len(matrix[0])  # 3 (количество столбцов в первой строке)

# Всех элементов
total = sum(len(row) for row in matrix)  # 9

# Или через functools.reduce
from functools import reduce
total = reduce(lambda acc, row: acc + len(row), matrix, 0)  # 9

Заключение

len() измеряет количество элементов в sizing объектах: строки, списки, кортежи, словари, множества, байты, диапазоны. Это работает через метод len(). Для производительности len() должна быть O(1). Генераторы, итераторы и числа не имеют len(), потому что их размер неизвестен или не имеет смысла. Используй if obj: вместо if len(obj) > 0: для проверки пустоты.