为什么列表询问__len__? [英] Why does list ask about __len__?

查看:81
本文介绍了为什么列表询问__len__?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

class Foo:
    def __getitem__(self, item):
        print('getitem', item)
        if item == 6:
            raise IndexError
        return item**2
    def __len__(self):
        print('len')
        return 3

class Bar:
    def __iter__(self):
        print('iter')
        return iter([3, 5, 42, 69])
    def __len__(self):
        print('len')
        return 3

演示:

>>> list(Foo())
len
getitem 0
getitem 1
getitem 2
getitem 3
getitem 4
getitem 5
getitem 6
[0, 1, 4, 9, 16, 25]
>>> list(Bar())
iter
len
[3, 5, 42, 69]

为什么list调用__len__?似乎没有将结果用于任何显而易见的事情. for循环不会执行此操作. 迭代器协议中没有提及任何内容,该协议仅讨论了__iter____next__.

Why does list call __len__? It doesn't seem to use the result for anything obvious. A for loop doesn't do it. This isn't mentioned anywhere in the iterator protocol, which just talks about __iter__ and __next__.

这是Python预先为列表保留空间还是类似的东西?

Is this Python reserving space for the list in advance, or something clever like that?

(Linux上的CPython 3.6.0)

(CPython 3.6.0 on Linux)

推荐答案

请参见 PEP 424的理性销售"部分介绍了__length_hint__,并提供了动机方面的见解:

See the Rationale section from PEP 424 that introduced __length_hint__ and offers insight on the motivation:

根据__length_hint__的估计,能够根据预期的大小预分配列表可能是一项重大优化. 已经观察到CPython比PyPy运行某些代码更快,这完全是因为存在这种优化.

Being able to pre-allocate lists based on the expected size, as estimated by __length_hint__ , can be a significant optimization. CPython has been observed to run some code faster than PyPy, purely because of this optimization being present.

除此之外,object.__length_hint__ >验证这纯粹是一项优化功能的事实:

In addition to that, the documentation for object.__length_hint__ verifies the fact that this is purely an optimization feature:

被调用以实现operator.length_hint().应该返回对象的估计长度(可以大于或小于实际长度).长度必须是整数>= 0. 此方法完全是一种优化,并且从不要求其正确性.

Called to implement operator.length_hint(). Should return an estimated length for the object (which may be greater or less than the actual length). The length must be an integer >= 0. This method is purely an optimization and is never required for correctness.

所以__length_hint__在这里是因为它可以带来一些不错的优化.

So __length_hint__ is here because it can result in some nice optimizations.

PyObject_LengthHint首先尝试从object.__len__(如果已定义),然后尝试查看object.__length_hint__是否可用.如果两者都不存在,它将为列表返回默认值8.

PyObject_LengthHint, first tries to get a value from object.__len__ (if it is defined) and then tries to see if object.__length_hint__ is available. If neither is there, it returns a default value of 8 for lists.

listextend根据此PEP进行了修改,以为定义__len____length_hint__的任何内容提供此优化.

listextend, which is called from list_init as Eli stated in his answer, was modified according to this PEP to offer this optimization for anything that defines either a __len__ or a __length_hint__.

list并非唯一受益于此的人,当然, bytes对象可以:

list isn't the only one that benefits from this, of course, bytes objects do:

>>> bytes(Foo())
len
getitem 0
...
b'\x00\x01\x04\t\x10\x19'

所以bytearray对象,但是,只有当您extend他们:

so do bytearray objects but, only when you extend them:

>>> bytearray().extend(Foo())
len
getitem 0
...

tuple对象,它们创建填充自己:

and tuple objects which create an intermediary sequence to populate themselves:

>>> tuple(Foo())
len
getitem 0
...
(0, 1, 4, 9, 16, 25)


如果有人在徘徊,为什么在类Bar中的之前 'len'确切地打印了'iter',而不是在类Foo的情况下出现'iter':


If anybody is wandering why exactly 'iter' is printed before 'len' in class Bar and not after as happens with class Foo:

这是因为如果手中的对象定义了__iter__ Python 将首先调用它以获取迭代器,从而也运行print('iter').如果退回到使用__getitem__,则不会发生同样的事情.

This is because if the object in hand defines an __iter__ Python will first call it to get the iterator, thereby running the print('iter') too. The same doesn't happen if it falls back to using __getitem__.

这篇关于为什么列表询问__len__?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆