有没有某种情况下,len(someObj)不会调用someObj的__len__函数?

14

len(someObj)在任何情况下都会调用someObj__len__函数吗?

最近我将前者替换为后者,以(成功地)加速某些代码。我想确保没有某个边缘情况,在这种情况下len(someObj)不等同于someObj.__len__()

5个回答

18
如果__len__返回的长度超过sys.maxsizelen()会引发异常。但如果直接调用__len__,并非如此。(事实上,您可以从__len__返回任何对象,除非它经过len(),否则不会被捕获。)

3
иҜ·жіЁж„ҸпјҢз”ұдәҺlenеә”иҜҘиҝ”еӣһйӣҶеҗҲдёӯе…ғзҙ зҡ„ж•°йҮҸпјҢеӣ жӯӨиҝ”еӣһжҜ”sys.maxsizeжӣҙеӨ§зҡ„еҖјеҮ д№ҺиӮҜе®ҡжҳҜж— ж„Ҹд№үзҡ„гҖӮ - Mike Graham
2
@Mike 理论上,你可以拥有一个像 Python 3 的 range 一样的对象,它不会将所有元素存储在内存中,并使用数学计算其 __len__。 在这种情况下,range.__len__ 本身会引发错误:range(sys.maxsize+1).__len__() 会产生 OverflowError: Python int too large to convert to C ssize_t - wjandrea
2
请注意,上述内容仅适用于2.x版本。例如,在3.6中,我得到len(range(1000000000000)) -> 1000000000000,而且(令人担忧的是)range(1000000000000).__len__() -> -727379968。尽管此结果仍然说明了为什么不应该自己调用__len__ - Karl Knechtel
如果__len__返回的值为负数或不是整数,len也会引发异常。 - kaya3

12
你看到了什么样的加速?我无法想象它是否明显。来自http://mail.python.org/pipermail/python-list/2002-May/147079.html 在某些情况下没有区别,但是使用len()有几个优点。首先,不建议自己调用__methods__,它们是由Python的其他部分使用的。len()将适用于任何类型的序列对象(列表、元组和所有)。__len__只能在具有__len__方法的类实例上工作。len()将在没有长度的对象上返回更合适的异常。

这个程序运行了一分钟,但其中大约有半秒钟的时间是因为我调用了2,443,519次len函数。在写问题的过程中,我意识到应该减少调用len函数的次数。 - David Locke
@David:是的,你漏掉了2,443,519这部分。天哪 ;) - Crescent Fresh
我个人认为,即使能够提高1/120的速度,也不值得因代码丑陋而去做。但这是你的决定。 - Eli Courtwright
@Eli,通常我会同意您的看法。但在这种情况下,我正在尝试在多种语言中对同一个问题进行基准测试。 - David Locke
1
顺便说一下:我成功地删除了 2,363,276 次 len 调用,这又使速度提高了一秒半。 - David Locke
1
@DavidLocke:我认为基于每种语言的惯用代码的基准比基于扭曲和混乱代码的基准更有用。 - Ethan Furman

3
我认为答案是它总是能够工作的 - 根据Python文档:
__len__(self):

该内置函数用于实现len()。应返回对象的长度,一个整数>=0。同时,在布尔上下文中,如果一个对象没有定义__nonzero__()方法并且其__len__()方法返回零,则被视为false。


2

有些情况下,len(someObj)someObj.__len__() 不同,因为 len() 验证了 __len__() 的返回值。以下是 Python 3.6.9 中可能出现的错误:

  • 太低了,即小于0

    ValueError: __len__() 应该返回 >= 0
    
  • 太高了,即大于 sys.maxsize(仅适用于CPython,参见docs

    OverflowError: 无法将 'int' 转换为索引大小的整数
    
  • 无效的类型,例如float

    TypeError: 'float' 对象无法被解释为整数
    
  • 缺失,例如len(object)

    TypeError: 类型为 'type' 的对象没有 len()
    

    我提到这个是因为object.__len__()会引发不同的异常,即AttributeError

值得注意的是,range(sys.maxsize+1) 是有效的,但它的 __len__() 会引发异常:
OverflowError: Python int too large to convert to C ssize_t

-4
根据Mark Pilgrim的说法,看起来不是这样的。len(someObj)someObj.__len__()相同;
干杯!

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接