6

在 for 循环上下文中使用列表理解或in关键字时,即:

for o in X:
    do_something_with(o)

或者

l=[o for o in X]
  • 背后的机制如何in运作?
  • 它调用了哪些函数\方法X
  • 如果X可以遵守不止一种方法,优先级是什么?
  • 如何编写一个高效的X,以便列表理解会很快?
4

6 回答 6

10

的,afaik,完整和正确的答案。

for,在 for 循环和列表推导中,都调用iter(). 如果有方法或方法,将返回一个可迭代的。如果它同时实现,则使用。如果它既没有你得到。Xiter()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__. list一个更简单的方法是使用yield,并制作__iter__成一个生成器:

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

    def __iter__(self):
        for x in self.data:
            yield x

最后一种是我推荐的方式。简单高效。

于 2011-01-30T17:47:29.797 回答
5

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

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

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

于 2011-01-30T16:41:20.203 回答
4

回答问题的评论我可以说在这种情况下阅读来源并不是最好的主意。对于第一次看到 Python 源代码的人来说,负责执行编译代码 ( ceval.c ) 的代码似乎不是很冗长。这是代表 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 之类的网站是首选,而应仅检查源代码以获取未发现的实现细节。

于 2011-01-30T17:46:59.980 回答
3

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

因此,要开始for..in循环或列表推导,首先调用 firstX__iter__()方法来获取迭代器对象;然后为每次迭代调用该对象的next()方法,直到StopIteration引发,此时迭代停止。

我不确定您的第三个问题是什么意思,以及如何为您的第四个问题提供有意义的答案,除非您的迭代器不应一次在内存中构建整个列表。

于 2011-01-30T16:42:44.093 回答
2

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

在幕后,for 语句在容器对象上调用 iter()。该函数返回一个迭代器对象,该对象定义了 next() 方法,该方法一次访问容器中的一个元素。当没有更多元素时,next() 会引发一个 StopIteration 异常,告诉 for 循环终止。

于 2011-01-30T16:40:05.603 回答
0

要回答您的问题:

背后的机制是如何运作的?

正如其他人已经指出的那样,它与用于普通 for 循环的机制完全相同。

它调用了 X 中的哪些函数\方法?

正如下面的评论中所指出的,它调用iter(X)获取迭代器。如果定义X了方法函数__iter__(),则调用 this 以返回迭代器;否则,如果X定义了__getitem__(),这将被重复调用以迭代X。请参阅iter()此处的 Python 文档:http: //docs.python.org/library/functions.html#iter

如果 X 可以遵守不止一种方法,那么优先级是什么?

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

新型 Python 类中的方法解析顺序 (MRO)

如何编写一个高效的 X,以便列表理解很快?

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

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

于 2011-01-30T17:00:53.340 回答