为啥 Python 的 'len' 函数比 __len__ 方法快?

Posted

技术标签:

【中文标题】为啥 Python 的 \'len\' 函数比 __len__ 方法快?【英文标题】:Why is Python's 'len' function faster than the __len__ method?为什么 Python 的 'len' 函数比 __len__ 方法快? 【发布时间】:2013-12-16 15:24:14 【问题描述】:

在 Python 中,len 是一个函数,通过调用对象的 __len__ 方法来获取集合的长度:

def len(x):
    return x.__len__()

所以我希望直接调用__len__() 至少与len() 一样快。

import timeit

setup = '''
'''

print (timeit.Timer('a="12345"; x=a.__len__()', setup=setup).repeat(10))
print (timeit.Timer('a="12345"; x=len(a)',      setup=setup).repeat(10))

Demo link

但使用上述代码测试的结果显示len() 更快。为什么?

【问题讨论】:

Profiled performance of len(set) vs. set.__len__() in Python 3的可能重复 @GamesBrainiac 这个问题是关于相反的观察。 旁白:使用setup 的目的是确保您只对您感兴趣的内容进行计时。如果您想设置a + 的时间,而不是获取长度,那很好,但你根本不需要setup 如果长度不变,需要多次查找,则将其存储为整数n = len(a),并使用n 【参考方案1】:

来自 Steven F. Lott 和 Dusty Phillips 的优秀 Python Object-Oriented Programming: Build robust and maintainable object-oriented Python applications and libraries, 4th Edition 书籍

您可能想知道为什么这些对象没有长度属性,而不必对它们调用函数。从技术上讲,他们确实如此。 len() 将适用的大多数对象都有一个名为 __len__() 的方法,该方法返回相同的值。所以len(myobj) 似乎调用了myobj.__len__()

为什么我们应该使用len() 函数而不是__len__() 方法?显然,__len__() 是一个特殊的双下划线方法,建议我们不要直接调用它。对此必须有一个解释。 Python 开发人员不会轻易做出这样的设计决定。

主要原因是效率。当我们调用对象的__len__() 方法时,对象必须在其命名空间中查找该方法,并且,如果特殊的__getattribute__() 方法(每次访问对象上的属性或方法时都会调用该方法)是在该对象上定义,它也必须被调用。此外,__getattribute__() 方法可能是为了做一些聪明的事情而编写的,例如,拒绝让我们访问诸如 __len__() 之类的特殊方法! len() 函数不会遇到任何这种情况。它实际上调用了底层类的__len__() 方法,所以len(myobj) 映射到MyObj.__len__(myobj)

【讨论】:

【参考方案2】:

__len__len() 慢,因为__len__ 涉及字典查找。

【讨论】:

这引发了len 如何避免 dict 查找的问题,我认为它对 OP(或其他任何不知道如何操作的人)没有特别有用的解释,因为好吧。【参考方案3】:

内置的len() 函数不查找.__len__ 属性。它查找tp_as_sequence pointer,而后者又具有sq_length attribute。

内置对象上的.__len__ 属性是间接mapped to the same slot,正是这种间接(加上属性查找)需要更多时间。

对于 Python 定义的类,type 对象在请求 sq_length 时查找 .__len__ 方法。

【讨论】:

以上是关于为啥 Python 的 'len' 函数比 __len__ 方法快?的主要内容,如果未能解决你的问题,请参考以下文章

为啥向量长度 SIMD 代码比普通 C 慢

为啥 len(None) 不返回 0?

为什么hashlib比sha256的其他代码更快?如何让我的代码接近hashlib性能?

Python设置交集比Rust HashSet交集更快

Python_初识函数和返回值_22

铁乐学python_day09_作业