考虑以下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__
的次数是实现细节。之所以现在被调用两次是因为是对预分配逻辑的额外检查。。。