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 循环如何评估其参数的主要内容,如果未能解决你的问题,请参考以下文章
斯坦福大学公开课机器学习: advice for applying machine learning - evaluatin a phpothesis(怎么评估学习算法得到的假设以及如何防止过拟合或欠