在for循环上下文中使用list comprehension或in
关键字时,即:
for o in X:
do_something_with(o)
或
l=[o for o in X]
in
背后的机制如何运作? X
中有哪些函数\方法可以调用? X
符合多种方法,那么优先级是多少? X
,以便列表理解能够快速完成?答案 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 ()创建生成器函数。以下是对生成器的讨论: