ZetCode

Python __len__ 方法

最后修改于 2025 年 4 月 8 日

本综合指南将探讨 Python 的 `__len__` 方法,这是一个允许对象定义其长度的特殊方法。我们将涵盖基本用法、序列协议、自定义容器和实际示例。

基本定义

`__len__` 方法返回对象的长度(项目数)。它由内置的 `len()` 函数调用,并应返回一个非负整数。

主要特点:必须为序列和映射类型实现;应高效(O(1) 复杂度);返回整数 ≥ 0。该方法使对象能够与 Python 的内置函数和运算符一起使用。

基本 __len__ 实现

这是一个实现 `__len__` 的简单类,用于演示其基本用法。该方法允许实例与 `len()` 函数一起使用。

basic_len.py
class ShoppingCart:
    def __init__(self):
        self.items = []
    
    def add_item(self, item):
        self.items.append(item)
    
    def __len__(self):
        return len(self.items)

cart = ShoppingCart()
cart.add_item("Apple")
cart.add_item("Banana")
print(len(cart))  # Output: 2

此示例展示了一个跟踪项目的购物篮类。 `__len__` 方法委托给内部项目列表的长度。当在购物车实例上调用 `len()` 时,Python 会调用 `__len__`。

实现应快速,因为 `len()` 经常用于性能关键的代码。Python 期望 `__len__` 快速返回。

实现固定大小的容器

对于具有预定大小的容器, `__len__` 可以返回固定值。此示例显示了一个始终报告相同长度的固定大小的队列。

fixed_size.py
class FixedQueue:
    def __init__(self, size):
        self.size = size
        self._items = [None] * size
        self._pointer = 0
    
    def enqueue(self, item):
        self._items[self._pointer] = item
        self._pointer = (self._pointer + 1) % self.size
    
    def __len__(self):
        return self.size

queue = FixedQueue(5)
queue.enqueue("A")
queue.enqueue("B")
print(len(queue))  # Always returns 5

这个固定大小的队列维护一个恒定的容量。 `__len__` 方法返回预定大小而不是当前项目数。

当逻辑大小与物理存储大小不同时,此模式很有用。该方法反映了容器的设计而不是其当前状态。

具有惰性求值的自定义集合

对于长度计算开销大的集合, `__len__` 可以实现缓存或惰性求值来优化性能。

lazy_length.py
class LargeDataset:
    def __init__(self, data_source):
        self.data_source = data_source
        self._length = None
    
    def __len__(self):
        if self._length is None:
            print("Calculating length...")
            self._length = sum(1 for _ in self.data_source)
        return self._length

data = LargeDataset(open('large_file.txt'))
print(len(data))  # Calculates on first call
print(len(data))  # Uses cached value

此类表示一个大型数据集,其中计算长度非常昂贵。 `__len__` 方法在第一次计算后缓存结果。

惰性求值模式对于文件或数据库查询等资源非常宝贵,因为确定长度需要大量的计算或 I/O。

实现位掩码

对于非传统的集合, `__len__` 可以返回逻辑大小而不是物理大小。此示例显示了一个报告活动位的位掩码类。

bitmask.py
class Bitmask:
    def __init__(self, size):
        self.size = size
        self.mask = 0
    
    def set_bit(self, position):
        self.mask |= (1 << position)
    
    def __len__(self):
        return bin(self.mask).count('1')

mask = Bitmask(8)
mask.set_bit(2)
mask.set_bit(5)
print(len(mask))  # Returns 2 (bits set)

此位掩码类将设置的位数计为其长度。 `__len__` 方法将掩码转换为二进制并计算“1”字符。

这演示了 `__len__` 如何以特定于域的方式表示逻辑大小。该方法提供了一致的接口,无论内部表示如何。

具有递归长度的树结构

对于递归数据结构, `__len__` 可以实现递归计数。此示例显示了一个计算所有后代的树节点类。

tree_length.py
class TreeNode:
    def __init__(self, value):
        self.value = value
        self.children = []
    
    def add_child(self, node):
        self.children.append(node)
    
    def __len__(self):
        return 1 + sum(len(child) for child in self.children)

root = TreeNode("Root")
child1 = TreeNode("Child1")
child2 = TreeNode("Child2")
grandchild = TreeNode("Grandchild")
child1.add_child(grandchild)
root.add_child(child1)
root.add_child(child2)
print(len(root))  # Returns 4 (root + 2 children + 1 grandchild)

此树实现计算层次结构中的所有节点。每个节点贡献 1 加上其子节点的长度。递归处理任意深度。

对于非常深的结构,请考虑迭代实现以避免堆栈溢出。该方法为嵌套数据提供直观的大小报告。

最佳实践

资料来源

作者

我叫 Jan Bodnar,是一名充满热情的程序员,拥有丰富的编程经验。自 2007 年以来,我一直在撰写编程文章。迄今为止,我已撰写了 1,400 多篇文章和 8 本电子书。我在教学编程方面拥有十多年的经验。

列出所有 Python 教程