阅读文档我注意到内置函数len
不支持所有迭代,只支持序列和映射(和集合)。在阅读之前,我一直认为len
函数使用迭代协议来评估对象的长度,所以我真的很惊讶于阅读它。
我已阅读已发布的问题(here和here),但我仍然感到困惑,我仍然没有找到为什么不允许len
工作的真正原因一般的所有迭代。
这是一个比实施原因更具概念/逻辑性的理由吗?我的意思是,当我问一个物体的长度时,我要求一个属性(它有多少元素),一个像生成器那样对象的属性,因为它们没有元素在里面,生成元素。
此外,生成器对象可以产生无限长度,导致未定义的长度,这是其他对象不能发生的事情,如列表,元组,dicts等......
我是对的,还是有更多的见解/更多我不考虑的事情?
答案 0 :(得分:8)
最大的原因是降低了类型安全。
你有多少程序在你实际需要 消费一个可迭代的地方写了一下,只知道它有多少元素,扔掉了别的东西?
我,在用Python编写的相当多年里,从不需要它。这是正常程序中的非感性操作。迭代器可能没有长度(例如,期望通过send()
输入的无限迭代器或生成器),因此要求它没有多大意义。 len(an_iterator)
产生错误的事实意味着您可以在代码中找到错误。你可以看到在程序的某个部分你正在调用len
的错误的东西,或者你的函数实际上需要一个序列而不是你期望的迭代器。
删除这些错误会产生一类新的错误,人们在调用len
时会错误地使用迭代器,或者使用迭代器,就好像它是一个没有实现的序列一样。
如果你真的需要知道迭代器的长度,len(list(iterator))
有什么问题?额外的6个字符?编写适用于迭代器的自己的版本是微不足道的,但正如我所说,99%的时间这只是意味着代码中的内容是错误的,因为这样的操作不会#39; t很有意义。
第二个原因是,通过该更改,您违反了当前适用于所有(已知)容器的len
两个不错的属性:
众所周知,在Python中实现的 所有 容器都很便宜(所有内置插件,标准库,{ {1}}& numpy
和所有其他大型第三方库都在动态大小和静态大小的容器上执行此操作。因此,当您看到scipy
时,您知道len(something)
电话便宜。使它与迭代器一起工作意味着由于计算长度,所有程序突然变得低效。
另请注意,您可以在每个容器上实现O(1)len
。预先计算长度的成本通常可以忽略不计,通常值得付费。
唯一的例外是,如果您实现 immutable 容器,其内部表示的一部分与其他实例共享(以节省内存)。但是,我不知道任何执行此操作的实现,并且大多数时候您可以实现比O(n)时间更好的实现。
总结:目前每个人在O(1)中实施__len__
,而 继续这样做。所以对__len__
的调用有一个期望为O(1)。即使它不是标准的一部分。 Python开发人员故意在文档中避免使用C / C ++的样式,并信任用户。在这种情况下,如果您的len
不是O(1),则预计会记录该内容。
众所周知,不具破坏性。 __len__
的任何明智的实现都不会改变其论点。因此,您可以确定__len__
或len(x) == len(x)
。
即使这个属性也没有在文档中定义,但是每个人都期望它,并且目前没有人违反它。
这样的属性很好,因为你可以使用它们来推理和假设代码。 它们可以帮助您确保一段代码的正确性,或者了解它的渐近复杂性。你提出的改变会使得查看某些代码变得更加困难并理解它是否正确或者它的复杂性是什么,因为你必须记住特殊情况。
总之,你提出的改变有一个,非常小的专业:在非常特殊的情况下保存很少的字符,但它有几个很大的缺点会影响现有代码的很大一部分。
另一个小原因。如果n = len(x);len(list(x)) == n
使用迭代器我确定某些人会因为其副作用而开始滥用它(取代已经丑陋的len
使用或列表推导)。突然间,人们可以编写如下代码:
map
打印文字,真的很难看。它读起来并不好。有状态代码应该与语句相关联,因为它们提供了副作用的视觉提示。