Как переопределить метод `__len__` для классов, которые представляют коллекции?

Для переопределения метода __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() будет работать с экземплярами вашего класса, представляющего коллекцию. Этот метод должен возвращать целое число, представляющее "длину" коллекции.

Принцип переопределения:

  1. Создайте свой класс, представляющий коллекцию (например, список, набор, словарь, или свою собственную структуру данных).
  2. Внутри определения класса переопределите (override) метод __len__(self).
  3. Внутри метода __len__(self) реализуйте логику для вычисления и возврата "длины" коллекции. Это может быть количество элементов в списке, размер словаря, количество узлов в связанном списке и т.д.
  4. Метод __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__`.

0