for 循环如何评估其参数

Posted

技术标签:

【中文标题】for 循环如何评估其参数【英文标题】:How does a for loop evaluate its argument 【发布时间】:2016-05-28 03:30:56 【问题描述】:

我的问题很简单。

for 循环是否每次都会评估它使用的参数?

如:

for i in range(300):

python 是否为这个循环的每次迭代创建一个包含 300 个项目的列表?

如果是,这是避免它的方法吗?

lst = range(300)
for i in lst:
    #loop body

类似这样的代码示例也是如此。

for i in reversed(lst):

for k in range(len(lst)):

是每次都应用反向过程,还是每次迭代都计算长度? (我对 python2 和 python3 都要求这个)

如果不是,Python 在迭代时如何评估可迭代对象的更改?

【问题讨论】:

如果您对优化感兴趣,我会查看“将代码转换为漂亮的惯用 Python” - youtube.com/watch?v=OSGv2VnC0go - 很多很棒的 Python 代码。 你已经得到了答案,但值得一提的是,在 Python 2 中 range() 只返回一个列表。在 Python 3 中,range(300) 创建了一个范围对象,它是一个具有内部状态的生成器。这意味着您可以在不耗尽内存的情况下执行range(1000000000000000000) 之类的操作。在 Python 2 中,您可以使用xrange 来获得相同的效果。 【参考方案1】:

创建什么对象取决于您循环返回的 Iterable 的 __iter__ 方法。

当迭代一个本身不是迭代器的迭代器时,Python 通常会创建一个迭代器。在 Python2 中,range 返回一个列表,它是一个 Iterable,并且有一个 __iter__ 方法返回一个 Iterator。

>>> from collections import Iterable, Iterator
>>> isinstance(range(300), Iterable)
True
>>> isinstance(range(300), Iterator)
False
>>> isinstance(iter(range(300)), Iterator)
True

for in sequence: do something 语法基本上是执行此操作的简写:

it = iter(some_iterable) # get Iterator from Iterable, if some_iterable is already an Iterator, __iter__ returns self by convention
while True:
    try:
        next_item = next(it)
        # do something with the item
    except StopIteration:
        break

这是一个演示,其中包含一些打印语句,以阐明使用 for 循环时发生的情况:

class CapitalIterable(object):
    'when iterated over, yields capitalized words of string initialized with'
    def __init__(self, stri):
        self.stri = stri

    def __iter__(self):
        print('__iter__ has been called')
        return CapitalIterator(self.stri)

        # instead of returning a custom CapitalIterator, we could also
        # return iter(self.stri.title().split())
        # because the built in list has an __iter__ method

class CapitalIterator(object):
    def __init__(self, stri):
        self.items = stri.title().split()
        self.index = 0

    def next(self): # python3: __next__
        print('__next__ has been called')
        try:
            item = self.items[self.index]
            self.index += 1
            return item
        except IndexError:
            raise StopIteration

    def __iter__(self):
        return self

c = CapitalIterable('The quick brown fox jumps over the lazy dog.')
for x in c:
    print(x)

输出:

__iter__ has been called
__next__ has been called
The
__next__ has been called
Quick
__next__ has been called
Brown
__next__ has been called
Fox
__next__ has been called
Jumps
__next__ has been called
Over
__next__ has been called
The
__next__ has been called
Lazy
__next__ has been called
Dog.
__next__ has been called

如您所见,__iter__ 只被调用一次,因此只创建了一个 Iterator 对象。

【讨论】:

【参考方案2】:

Range 在这种情况下会创建一个包含 300 个整数的数组。它不会创建 300 个整数的数组 300 次。它不是每一个有效的。如果您使用 xrange ,它将创建一个不会占用几乎一样多内存的可迭代对象。 https://docs.python.org/2/library/functions.html#xrange

example.py

​​>
for i in xrange(300): #low memory foot print, similar to a normal loop
    print(i)

【讨论】:

【参考方案3】:

不用担心,迭代器只会被评估一次。它最终大致相当于这样的代码:

it = iter(range(300))
while True:
    try:
        i = next(it)
    except StopIteration:
        break
    ... body of loop ...

请注意,它不是相当等效的,因为break 的工作方式会有所不同。请记住,您可以将 else 添加到 for 循环中,但这在上面的代码中不起作用。

【讨论】:

或者在 Python 2 或 Python 3 中,i = next(it) 另外,虽然在整个循环中使用的是同一个 iterator,但它正在迭代的东西可能会改变,这就是为什么不建议这样做的原因,例如,修改迭代其元素时的列表。 谢谢,我刚刚找到了一种方法来检查我问的内容。我可以只定义一个返回可迭代(不是生成器)的函数,在其中添加一个打印语句,而不是检查它会打印多少次。

以上是关于for 循环如何评估其参数的主要内容,如果未能解决你的问题,请参考以下文章

for循环求和中的for循环正在覆盖数据值python

斯坦福大学公开课机器学习: advice for applying machine learning - evaluatin a phpothesis(怎么评估学习算法得到的假设以及如何防止过拟合或欠

如何从 Python 代码进入 REPL(读取、评估、打印、循环)

如何使用类获取跨度内的文本。 (for循环内部)

如何在 for 循环中调用参数化构造函数。

Swift:如何将 for-in 循环与可选参数一起使用?