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