← Назад к вопросам
Что можно померить с помощью 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: для проверки пустоты.