str.join实现行为错误?

时间:2018-11-17 21:56:44

标签: python c python-3.x join cpython

考虑以下代码:

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)下对其进行了测试。

1 个答案:

答案 0 :(得分:1)

str.join的工作原理(通过分析源代码)

首先,它检查对象是否为可迭代对象,并根据需要从中创建一个序列

seq = PySequence_Fast(iterable, "can only join an iterable");

如果对象是listtuple,则仅返回对象本身,而无需进行迭代。

如果不是,则迭代创建一个list。那就是对象被完全迭代的地方。

从那里开始,仅使用list副本。 iterable已被迭代,如果不是listtuple,则现在已无用。

(我无法找到对len的调用,需要进行调试会话才能在PySequence_Fast调用中找到它,但这似乎没用。您的迭代器有一个__len__方法,可以,但是由于它不是listtuple,因此不使用返回值)