考虑以下代码:
class A(object):
def __init__(self):
self.a = '123'
def __len__(self):
print('len')
return 2
def __getitem__(self, pos):
print('get pos', pos)
return self.a[pos]
a = A()
print(''.join(a))
我的预期输出:
> len
> get pos 0
> get pos 1
> 12
实际输出:
> len
> get pos 0
> get pos 1
> get pos 2
> get pos 3
> 123
Try it your self.我不敢相信这里会发生什么。
据我正确理解行为, str.join()调用 __ len __ ,但忽略该值并调用 __ getItem __ ,直到索引超出范围异常。
我必须忽略一些事情,因为join的实现似乎有所不同:
https://github.com/python/cpython/blob/3.6/Objects/stringlib/join.h
我当前的解决方法是:
def __getitem__(self, pos):
if pos >= len(self):
raise IndexError()
return self.a[pos]
这太荒谬了。
我在Python 3.6和3.7(CPython)下对其进行了测试。
答案 0 :(得分:1)
str.join
的工作原理(通过分析源代码)
首先,它检查对象是否为可迭代对象,并根据需要从中创建一个序列
seq = PySequence_Fast(iterable, "can only join an iterable");
如果对象是list
或tuple
,则仅返回对象本身,而无需进行迭代。
如果不是,则迭代创建一个list
。那就是对象被完全迭代的地方。
从那里开始,仅使用list
副本。 iterable
已被迭代,如果不是list
或tuple
,则现在已无用。
(我无法找到对len
的调用,需要进行调试会话才能在PySequence_Fast
调用中找到它,但这似乎没用。您的迭代器有一个__len__
方法,可以,但是由于它不是list
或tuple
,因此不使用返回值)