Python:列表理解背后的机制

时间:2011-01-30 16:33:04

标签: python implementation list-comprehension language-implementation

在for循环上下文中使用list comprehension或in关键字时,即:

for o in X:
    do_something_with(o)

l=[o for o in X]
  • in背后的机制如何运作?
  • X中有哪些函数\方法可以调用?
  • 如果X符合多种方法,那么优先级是多少?
  • 如何编写高效的X,以便列表理解能够快速完成?

6 个答案:

答案 0 :(得分:10)

afaik,完整而正确的答案。

for,在for循环和列表推导中,都会在iter()上调用X。如果iter()具有X方法或__iter__方法,__getitem__将返回可迭代。如果它同时实现,则使用__iter__。如果它没有得到TypeError: 'Nothing' object is not iterable

这实现了__getitem__

class GetItem(object):
    def __init__(self, data):
        self.data = data

    def __getitem__(self, x):
        return self.data[x]

用法:

>>> data = range(10)
>>> print [x*x for x in GetItem(data)]
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

这是实施__iter__的一个例子:

class TheIterator(object):
    def __init__(self, data):
        self.data = data
        self.index = -1

    # Note: In  Python 3 this is called __next__
    def next(self):
        self.index += 1
        try:
            return self.data[self.index]
        except IndexError:
            raise StopIteration

    def __iter__(self):
        return self

class Iter(object):
    def __init__(self, data):
        self.data = data

    def __iter__(self):
        return TheIterator(data)

用法:

>>> data = range(10)
>>> print [x*x for x in Iter(data)]
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

如您所见,您需要实现迭代器和返回迭代器的__iter__

您可以将它们组合在一起:

class CombinedIter(object):
    def __init__(self, data):
        self.data = data

    def __iter__(self):
        self.index = -1
        return self

    def next(self):
        self.index += 1
        try:
            return self.data[self.index]
        except IndexError:
            raise StopIteration

用法:

>>> well, you get it, it's all the same...

但是那时你只能同时拥有一个迭代器。 好的,在这种情况下你可以这样做:

class CheatIter(object):
    def __init__(self, data):
        self.data = data

    def __iter__(self):
        return iter(self.data)

但这是作弊,因为你只是在重用__iter__的{​​{1}}方法。 一种更简单的方法是使用yield,并将list放入生成器中:

__iter__

这是我推荐的方式。简单高效。

答案 1 :(得分:5)

X必须是可迭代的。它必须实现__iter__(),它返回一个迭代器对象;迭代器对象必须实现next(),每次调用时都会返回下一个项目,如果没有下一个项目则会引发StopIteration

列表,元组和生成器都是可迭代的。

请注意,普通for运算符使用相同的机制。

答案 2 :(得分:4)

回答问题的评论我可以说在这种情况下阅读源不是最好的主意。负责执行已编译代码(ceval.c)的代码对于第一次看到Python源的人来说似乎并不十分冗长。以下是代表for循环迭代的代码段:

   TARGET(FOR_ITER)
        /* before: [iter]; after: [iter, iter()] *or* [] */
        v = TOP();

        /*
          Here tp_iternext corresponds to next() in Python
        */
        x = (*v->ob_type->tp_iternext)(v); 
        if (x != NULL) {
            PUSH(x);
            PREDICT(STORE_FAST);
            PREDICT(UNPACK_SEQUENCE);
            DISPATCH();
        }
        if (PyErr_Occurred()) {
            if (!PyErr_ExceptionMatches(
                            PyExc_StopIteration))
                break;
            PyErr_Clear();
        }
        /* iterator ended normally */
        x = v = POP();
        Py_DECREF(v);
        JUMPBY(oparg);
        DISPATCH();

要找到实际发生的事情,你需要深入研究一堆其他文件,这些文件的详细程度并不好。因此,我认为在这种情况下,像SO这样的文档和站点是第一个去的地方,而应该仅检查源的未覆盖的实现细节。

答案 3 :(得分:3)

X必须是可迭代的对象,这意味着它需要__iter__()方法。

因此,要启动for..in循环或列表推导,首先调用X的{​​{1}}方法来获取迭代器对象;然后在每次迭代时调用该对象的__iter__()方法,直到next()被引发,此时迭代停止。

我不确定你的第三个问题意味着什么,以及如何为你的第四个问题提供有意义的答案,只是你的迭代器不应该立即在内存中构建整个列表。

答案 4 :(得分:2)

也许这有帮助(教程http://docs.python.org/tutorial/classes.html第9.9节):

  

在幕后,for声明   在容器对象上调用iter()。   该函数返回一个迭代器   定义方法next()的对象   它访问中的元素   一次一个容器。当有   不再是元素,next()提出了一个   告诉StopIteration异常   for循环终止。

答案 5 :(得分:0)

回答你的问题:

背后的机制如何运作?

与普通for循环使用的机制完全相同,正如其他人已经注意到的那样。

X中的哪些函数\方法可以调用?

如下面的评论中所述,它调用iter(X)来获取迭代器。如果X定义了方法函数__iter__(),则会调用它来返回迭代器;否则,如果X定义__getitem__(),则会重复调用此值以迭代X。请在此处查看iter()的Python文档:http://docs.python.org/library/functions.html#iter

如果X可以符合多种方法,那么优先级是什么?

我不确定你的问题是什么,确切地说,但Python有关于如何解析方法名称的标准规则,并且在此处遵循它们。以下是对此的讨论:

Method Resolution Order (MRO) in new style Python classes

如何编写高效的X,以便列表理解能够快速完成?

我建议您在Python中阅读有关迭代器和生成器的更多信息。使任何类支持迭代的一种简单方法是为 iter ()创建生成器函数。以下是对生成器的讨论:

http://linuxgazette.net/100/pramode.html