python 究竟如何将任意对象转换为列表?

How exactly python converts arbitrary object into list?

文档 says:

The constructor builds a list whose items are the same and in the same order as iterable‘s items. iterable may be either a sequence, a container that supports iteration, or an iterator object. If iterable is already a list, a copy is made and returned, similar to iterable[:]...

但是如果我的 class A 有一个对象 a,它实现了 __iter____len____getitem__,这list(a) 使用接口来迭代我的对象,这背后的逻辑是什么?

我的快速试验让我感到困惑:

class A(object):
    def __iter__(self):
        print '__iter__ was called'
        return iter([1,2,3])
    def __len__(self):
        print '__len__ was called'
        return 3
    def __getitem__(self, index):
        print '__getitem(%i)__ was called' % index
        return index+1

a = A()
list(a)

产出

__iter__ was called
__len__ was called
[1, 2, 3]

A.__iter__ 是第一个调用的,好的。但是为什么 A.__len__ 被调用了呢?然后为什么 A.__getitem__ 没有被调用?

然后我__iter__变成了发电机

这改变了魔术方法调用的顺序!

class B(object):
    def __iter__(self):
        print '__iter__ was called'
        yield 1
        yield 2
        yield 3
    def __len__(self):
        print '__len__ was called'
        return 3
    def __getitem__(self, index):
        print '__getitem(%i)__ was called' % index
        return index+1      

b = B()
list(b)

产出

__len__ was called
__iter__ was called
[1, 2, 3]

为什么现在先调用 B.__len__?但是为什么没有调用B.__getitem__,而转换是用B.__iter__完成的?

最让我困惑的是为什么__len____iter__的调用顺序在AB的情况下是不同的?

调用顺序没有改变。 __iter__ 仍然首先被调用,但是当 __iter__ 是生成器时,调用 __iter__ 不会立即 运行 函数体。 print 仅在 next 被调用时发生。

__len__ 被调用是一个实现细节。 Python 想要提示为列表分配多少 space,因此在您的对象上 it calls _PyObject_LengthHint,如果对象支持它,则使用 len。预计在对象上调用 len 通常会很快并且没有可见的副作用。