__len__ в классах, представляющих коллекции, нужно определить метод с именем __len__, который принимает self в качестве аргумента и возвращает целое число, представляющее количество элементов в коллекции. Например:
  
class MyCollection:
    def __init__(self, data):
        self.data = data
    def __len__(self):
        return len(self.data)
# Пример использования
my_collection = MyCollection([1, 2, 3, 4, 5])
print(len(my_collection))  # Выведет: 5
  __len__ не определён, то попытка использовать функцию len() на экземпляре класса вызовет исключение TypeError.
Переопределение метода __len__ позволяет определить, как оператор len() будет работать с экземплярами вашего класса, представляющего коллекцию.  Этот метод должен возвращать целое число, представляющее "длину" коллекции.
Принцип переопределения:
__len__(self).__len__(self) реализуйте логику для вычисления и возврата "длины" коллекции.  Это может быть количество элементов в списке, размер словаря, количество узлов в связанном списке и т.д.__len__ должен всегда возвращать целое неотрицательное число (int). Если ваша логика этого не гарантирует, убедитесь, что возвращаемое значение приведено к типу int.  Возврат значений, отличных от целых чисел, приведет к ошибкам.Пример кода:
class MyList:
    def __init__(self, data):
        self.data = data
    def __len__(self):
        return len(self.data)  # Возвращаем длину внутреннего списка
# Пример использования
my_list = MyList([1, 2, 3, 4, 5])
print(len(my_list))  # Выведет: 5
class CustomString:
    def __init__(self, text):
        self.text = text
    def __len__(self):
        return len(self.text) # возвращаем длину строки
custom_string = CustomString("Hello, world!")
print(len(custom_string)) # Выведет 13
class EmptyCollection:
    def __len__(self):
        return 0 # Всегда возвращает 0
empty_collection = EmptyCollection()
print(len(empty_collection)) # Выведет 0
Важные моменты:
__len__ можно не определять.  В этом случае вызов len() для экземпляра класса приведет к ошибке TypeError.__len__ должен быть быстрым и эффективным.  Если вычисление длины требует сложных операций, рассмотрите возможность кэширования результата или использование альтернативных подходов.__len__ только для классов, которые логично представлять как коллекции элементов.Пример с кастомной структурой данных:
class Node:
    def __init__(self, data):
        self.data = data
        self.next = None
class LinkedList:
    def __init__(self):
        self.head = None
        self.size = 0  # Храним размер списка
    def append(self, data):
        new_node = Node(data)
        if not self.head:
            self.head = new_node
        else:
            current = self.head
            while current.next:
                current = current.next
            current.next = new_node
        self.size += 1
    def __len__(self):
        return self.size  # Возвращаем сохраненный размер
# Пример использования
linked_list = LinkedList()
linked_list.append(1)
linked_list.append(2)
linked_list.append(3)
print(len(linked_list))  # Выведет: 3
В последнем примере LinkedList, размер коллекции хранится в атрибуте `self.size`. Это позволяет методу `__len__` возвращать размер за O(1) время. Альтернативный подход - каждый раз при вызове len() проходить по всей структуре, что приведёт к O(n) времени. Важно учитывать требования к производительности при реализации `__len__`.