迭代器和生成器之间的区别是什么?举一些例子来说明你在什么时候使用每种情况会很有帮助。
当前回答
每个人都有一个非常漂亮和冗长的答案,我真的很感激。我只是想给那些在概念上还不太清楚的人一个简短的回答:
If you create your own iterator, it is a little bit involved - you have to create a class and at least implement the iter and the next methods. But what if you don't want to go through this hassle and want to quickly create an iterator. Fortunately, Python provides a short-cut way to defining an iterator. All you need to do is define a function with at least 1 call to yield and now when you call that function it will return "something" which will act like an iterator (you can call next method and use it in a for loop). This something has a name in Python called Generator
希望这能澄清一点。
其他回答
我用一种非常简单的方式专门为Python新手编写,尽管Python在本质上做了很多事情。
让我们从最基本的开始:
考虑一个列表,
l = [1,2,3]
让我们写一个等效函数:
def f():
return [1,2,3]
打印(l)的O /p: [1,2,3] & O /p打印(f()): [1,2,3]
让列表l可迭代:在python中,列表总是可迭代的,这意味着你可以在任何你想要的时候应用迭代器。
让我们在list上应用迭代器:
iter_l = iter(l) # iterator applied explicitly
让我们把一个函数设为可迭代的,也就是说,写一个等效的生成器函数。 在python中,只要你引入关键字yield;它变成了一个生成器函数,迭代器将隐式应用。
注意:每个生成器在应用隐式迭代器时总是可迭代的,这里隐式迭代器是关键 因此生成器函数将是:
def f():
yield 1
yield 2
yield 3
iter_f = f() # which is iter(f) as iterator is already applied implicitly
如果你观察到,一旦你让函数f成为一个生成器,它就已经是iter(f)
Now,
L是列表,在应用迭代器方法iter后,它变成, iter(左) F已经是iter(F),在应用迭代器方法“iter”它 变成iter(iter(f))也就是iter(f)
这有点像你将int类型转换为int(x)它已经是int类型并且它将保持int(x)
例如o/p:
print(type(iter(iter(l))))
is
<class 'list_iterator'>
别忘了这是Python而不是C或c++
因此,由上述解释得出的结论是:
列出l ~= iter(l) 生成函数f == iter(f)
可迭代对象是可以(自然地)迭代的对象。然而,要做到这一点,你将需要一个类似迭代器对象的东西,是的,术语可能令人困惑。可迭代对象包括__iter__方法,该方法将返回可迭代对象的迭代器对象。
迭代器对象是一个实现迭代器协议的对象——一组规则。在这种情况下,它必须至少有这两个方法:__iter__和__next__。__next__方法是一个提供新值的函数。__iter__方法返回迭代器对象。在更复杂的对象中,可能有单独的迭代器,但在更简单的情况下,__iter__返回对象本身(通常返回self)。
一个iterable对象是一个列表对象。它不是一个迭代器,但它有一个__iter__方法,返回一个迭代器。你可以直接以things.__iter__()的形式调用这个方法,或者使用iter(things)。
如果你想遍历任何集合,你需要使用它的迭代器:
things_iterator = iter(things)
for i in things_iterator:
print(i)
然而,Python会自动使用迭代器,这就是为什么你从来没有看到上面的例子。相反,你可以这样写:
for i in things:
print(i)
自己编写迭代器可能很乏味,所以Python有一个更简单的选择:生成器函数。生成器函数不是普通的函数。不是遍历代码并返回最终结果,而是延迟代码,函数立即返回一个生成器对象。
生成器对象类似于迭代器对象,因为它实现了迭代器协议。这对于大多数目的来说已经足够好了。在其他答案中有许多生成器的例子。
简而言之,迭代器是一个对象,它允许您迭代另一个对象,无论它是一个集合还是其他一些值的来源。生成器是一个简化的迭代器,它或多或少完成相同的工作,但更容易实现。
通常情况下,如果你只需要发电机,你会选择发电机。但是,如果您正在构建一个更复杂的对象,其中包含其他特性之间的迭代,则应该使用迭代器协议。
对于相同的数据,你可以比较两种方法:
def myGeneratorList(n):
for i in range(n):
yield i
def myIterableList(n):
ll = n*[None]
for i in range(n):
ll[i] = i
return ll
# Same values
ll1 = myGeneratorList(10)
ll2 = myIterableList(10)
for i1, i2 in zip(ll1, ll2):
print("{} {}".format(i1, i2))
# Generator can only be read once
ll1 = myGeneratorList(10)
ll2 = myIterableList(10)
print("{} {}".format(len(list(ll1)), len(ll2)))
print("{} {}".format(len(list(ll1)), len(ll2)))
# Generator can be read several times if converted into iterable
ll1 = list(myGeneratorList(10))
ll2 = myIterableList(10)
print("{} {}".format(len(list(ll1)), len(ll2)))
print("{} {}".format(len(list(ll1)), len(ll2)))
此外,如果检查内存占用,生成器占用的内存要少得多,因为它不需要同时将所有值存储在内存中。
生成器函数,生成器对象,生成器:
Generator函数就像Python中的常规函数一样,但它包含一个或多个yield语句。Generator函数是一个很好的工具,可以尽可能简单地创建Iterator对象。generator函数返回的Iterator对象也称为generator对象或generator。
在这个例子中,我创建了一个Generator函数,它返回一个Generator对象< Generator对象fib at 0x01342480>。就像其他迭代器一样,Generator对象可以在for循环中使用,也可以与从Generator返回下一个值的内置函数next()一起使用。
def fib(max):
a, b = 0, 1
for i in range(max):
yield a
a, b = b, a + b
print(fib(10)) #<generator object fib at 0x01342480>
for i in fib(10):
print(i) # 0 1 1 2 3 5 8 13 21 34
print(next(myfib)) #0
print(next(myfib)) #1
print(next(myfib)) #1
print(next(myfib)) #2
因此,生成器函数是创建Iterator对象的最简单方法。
迭代器:
每个生成器对象都是迭代器,反之亦然。如果自定义迭代器对象的类实现了__iter__和__next__方法(也称为迭代器协议),则可以创建自定义迭代器对象。
然而,使用生成器函数来创建迭代器要容易得多,因为它们简化了迭代器的创建,但是自定义迭代器给了你更多的自由,你也可以根据你的需求实现其他方法,如下面的例子所示。
class Fib:
def __init__(self,max):
self.current=0
self.next=1
self.max=max
self.count=0
def __iter__(self):
return self
def __next__(self):
if self.count>self.max:
raise StopIteration
else:
self.current,self.next=self.next,(self.current+self.next)
self.count+=1
return self.next-self.current
def __str__(self):
return "Generator object"
itobj=Fib(4)
print(itobj) #Generator object
for i in Fib(4):
print(i) #0 1 1 2
print(next(itobj)) #0
print(next(itobj)) #1
print(next(itobj)) #1
添加一个答案,因为现有的答案都没有专门解决官方文献中的困惑。
生成器函数是用yield而不是return定义的普通函数。当被调用时,生成器函数返回一个生成器对象,这是一种迭代器——它有一个next()方法。当调用next()时,将返回生成器函数产生的下一个值。
函数或对象都可以被称为“生成器”,这取决于你阅读的Python源文档。Python术语表表示生成器函数,而Python wiki表示生成器对象。Python教程成功地在三句话中暗示了这两种用法:
生成器是用于创建迭代器的简单而强大的工具。它们像常规函数一样编写,但在需要返回数据时使用yield语句。每次在它上调用next()时,生成器都会从停止的地方恢复(它会记住所有的数据值和最后执行的语句)。
前两句话用生成器函数标识生成器,而第三句话用生成器对象标识它们。
尽管存在这些困惑,但人们可以从Python语言参考中找到明确的最终答案:
yield表达式仅在定义生成器函数时使用,并且只能在函数定义的主体中使用。在函数定义中使用yield表达式足以导致该定义创建一个生成器函数,而不是普通函数。 当调用generator函数时,它返回一个称为generator的迭代器。然后,该生成器控制生成器函数的执行。
因此,在正式和精确的用法中,“generator”不合格指的是生成器对象,而不是生成器功能。
上面的参考资料是针对Python 2的,但Python 3语言参考资料也说了同样的事情。然而,Python 3术语表指出
发电机……通常指生成器函数,但在某些上下文中也可能指生成器迭代器。在意图不明确的情况下,使用完整的术语可以避免歧义。
推荐文章
- 证书验证失败:无法获得本地颁发者证书
- 当使用pip3安装包时,“Python中的ssl模块不可用”
- 无法切换Python与pyenv
- Python if not == vs if !=
- 如何从scikit-learn决策树中提取决策规则?
- 为什么在Mac OS X v10.9 (Mavericks)的终端中apt-get功能不起作用?
- 将旋转的xtick标签与各自的xtick对齐
- 为什么元组可以包含可变项?
- 如何合并字典的字典?
- 如何创建类属性?
- 不区分大小写的“in”
- 在Python中获取迭代器中的元素个数
- 解析日期字符串并更改格式
- 使用try和。Python中的if
- 如何在Python中获得所有直接子目录