自定义类型长度的python列表构造函数调用了两次



考虑以下python代码:

class View:
pass

v = View()
print(list(v))

运行时,会出现以下错误:

Traceback (most recent call last):
File "testing.py", line 35, in <module>
print(list(v))
TypeError: 'View' object is not iterable

有道理。让我们将__iter__添加到View

class View:
def __iter__(self):
for i in range(5):
yield i

这现在有效,并且正确地产生[0, 1, 2, 3, 4]

然而,考虑一下如果View定义了__len__方法会发生什么:

class View:
def __len__(self):
print("len called")
return 2
def __iter__(self):
for i in range(5):
yield i
v = View()
print(list(v))

运行时,会产生:

len called
len called
[0, 1, 2, 3, 4]

__len__不仅被调用了两次,而且似乎也不是"尊敬的";(我返回2作为长度,但我的__iter__产生5个值,列表正确地由5个值组成(

发生了什么事?(注意,这不是一个无聊的好奇心。在我的应用程序中,我有一个昂贵的__len__方法,调用两次会减慢初始化时间(

为什么列表会询问__len__?调用CCD_ 9方法以获得需要向列表分配多少空间的近似值。请注意,这只是一个近似,无论您是使用__iter__还是__getitem__迭代,长度都不绑定:

>>> class GetItemIter:
...     def __len__(self):
...             return 2
...     def __getitem__(self, index):
...             return [0, 1, 2][index]
...
>>> list(GetItemIter())
[0, 1, 2]
>>> class IterIter:
...     def __len__(self):
...             return 2
...     def __iter__(self):
...             return iter([0, 1, 2])
...
>>> list(IterIter())
[0, 1, 2]

从Python 3.8中,列表构造函数将两次检查长度。这是在错误跟踪器中,但可能被认为是一个实现细节:

我们可以在构造函数上__len__的次数是实现细节。之所以现在被调用两次是因为是对预分配逻辑的额外检查。。。

最新更新