[英]Why does a yield from inside __next__() return generator object?
I am using yield
to return the next value in the __next__()
function in my class. However it does not return the next value, it returns the generator object.我正在使用
yield
在我的 class 的__next__()
function 中返回下一个值。但是它不返回下一个值,它返回生成器 object。
I am trying to better understand iterators and yield
.我试图更好地理解迭代器和
yield
。 I might be doing it in the wrong way.我可能做错了。
Have a look.看一看。
class MyString:
def __init__(self,s):
self.s=s
def __iter__(self):
return self
def __next__(self):
for i in range(len(self.s)):
yield(self.s[i])
r=MyString("abc")
i=iter(r)
print(next(i))
This returns:这将返回:
generator object __next__ at 0x032C05A0
next
pretty much just calls __next__()
in this case.在这种情况
next
几乎只是调用__next__()
。 Calling __next__
on your object will start the generator and return it (no magic is done at this point).在你的对象上调用
__next__
将启动生成器并返回它(此时没有魔法完成)。
In this case, you might be able to get away with not defining __next__
at all:在这种情况下,您可能完全不定义
__next__
:
class MyString:
def __init__(self,s):
self.s=s
def __iter__(self):
for i in range(len(self.s)):
yield(self.s[i])
# Or...
# for item in self.s:
# yield item
If you wanted to use __iter__
and __next__
(to define an iterator rather than simply making an iterable ), you'd probably want to do something like this:如果你想使用
__iter__
和__next__
(定义一个迭代器而不是简单地创建一个iterable ),你可能想要做这样的事情:
class MyString:
def __init__(self,s):
self.s = s
self._ix = None
def __iter__(self):
return self
def __next__(self):
if self._ix is None:
self._ix = 0
try:
item = self.s[self._ix]
except IndexError:
# Possibly reset `self._ix`?
raise StopIteration
self._ix += 1
return item
Let's take a look at the purpose of the __next__
method.让我们来看看
__next__
方法的目的。 From the docs :从文档:
iterator.__next__()
迭代器.__next__()
Return the next item from the container.
从容器中返回下一个项目。 If there are no further items, raise the StopIteration exception.
如果没有其他项目,则引发 StopIteration 异常。
Now let's see what the yield
statement does.现在让我们看看
yield
语句的作用。 Another excerpt from the docs : 文档的另一个摘录:
Using a yield expression in a function's body causes that function to be a generator
在函数体中使用 yield 表达式会导致该函数成为生成器
And并且
When a generator function is called, it returns an iterator known as a generator.
当一个生成器函数被调用时,它返回一个称为生成器的迭代器。
Now compare __next__
and yield
: __next__
returns the next item from the container .现在比较
__next__
和yield
: __next__
返回容器中的下一项。 But a function containing the yield
keyword returns an iterator .但是包含
yield
关键字的函数返回一个迭代器。 Consequently, using yield
in a __next__
method results in an iterator that yields iterators.因此,在
__next__
方法中使用yield
一个产生迭代器的迭代器。
If you want to use yield
to make your class iterable, do it in the __iter__
method:如果您想使用
yield
使您的类可迭代,请在__iter__
方法中进行:
class MyString:
def __init__(self, s):
self.s = s
def __iter__(self):
for s in self.s:
yield s
The __iter__
method is supposed to return an iterator - and the yield
keyword makes it do exactly that. __iter__
方法应该返回一个迭代器——而yield
关键字使它完全做到了这一点。
For completeness, here is how you would implement an iterator with a __next__
method.为了完整
__next__
,下面是如何使用__next__
方法实现迭代器。 You have to keep track of the state of the iteration, and return the corresponding value.您必须跟踪迭代的状态,并返回相应的值。 The easiest solution is probably to increment an index every time
__next__
is called:最简单的解决方案可能是每次调用
__next__
增加一个索引:
class MyString:
def __init__(self,s):
self.s = s
self.index = -1
def __iter__(self):
return self
def __next__(self):
self.index += 1
if self.index >= len(self.s):
raise StopIteration
return self.s[self.index]
As far as I can tell, generator functions are just syntactic sugar for classes with a next function.据我所知,生成器函数只是具有next函数的类的语法糖。 Example:
示例:
>>> def f():
i = 0
while True:
i += 1
yield i
>>> x = f()
>>> x
<generator object f at 0x0000000000659938>
>>> next(x)
1
>>> next(x)
2
>>> next(x)
3
>>> class g(object):
def __init__(self):
self.i = 0
def __next__(self):
self.i += 1
return self.i
>>> y = g()
>>> y
<__main__.g object at 0x000000000345D908>
>>> next(y)
1
>>> next(y)
2
>>> next(y)
3
In fact, I came here looking to see if there is any significant difference.其实,我来这里是想看看是否有任何显著差异。 Please shout if there is.
有的话请喊一声。
So, to answer the question, what you have is a class with a __next__ method that returns an object that also has a __next__ method.因此,要回答这个问题,您拥有的是一个具有 __next__ 方法的类,该类返回一个也具有 __next__ 方法的对象。 So the simplest thing to do would be to replace your
yield
with a return
and to keep track of how far along you are, and to remember to raise a StopIteration when you reach the end of the array.因此,最简单的做法是用
return
替换您的yield
并跟踪您走了多远,并记住在到达数组末尾时引发 StopIteration。 So something like:所以像:
class MyString:
def __init__(self,s):
self.s=s
self._i = -1
def __iter__(self):
return self
def __next__(self):
self._i += 1
if self._i >= len(self.s):
raise StopIteration
return self.s[self._i]
That's probably the simplest way to achieve what I think you're looking for.这可能是实现我认为您正在寻找的最简单方法。
If next()
function calls __next__()
method, what's happening in the following example.如果
next()
function 调用__next__()
方法,下面的示例中会发生什么。
Code:代码:
class T:
def __init__(self):
self.s = 10
def __iter__(self):
for i in range(self.s):
yield i
def __next__(self):
print('__next__ method is called.')
if __name__== '__main__':
obj = T()
k = iter(obj)
print(next(k)) #0
print(next(k)) #1
print(next(k)) #2
print(next(k)) #3
print(next(k)) #4
print(next(k)) #5
print(next(k)) #6
print(next(k)) #7
print(next(k)) #8
print(next(k)) #9
print(next(k))
print(next(k))
Terminal:终端:
C:...>python test.py
0
1
2
3
4
5
6
7
8
9
Traceback (most recent call last):
File "test.py", line 25, in <module>
print(next(k))
StopIteration
It seams that next()
function does not calling __next__
method.它接缝
next()
function 不调用__next__
方法。 I cannot understand why python docs states that " next(iterator, default)
Retrieve the next item from the iterator by calling its __next__()
method."我不明白为什么python 文档指出“
next(iterator, default)
通过调用它的__next__()
方法从迭代器中检索下一个项目。” If someonw knows, let us help!如果有人知道,让我们帮忙!
__iter__
with __next__
in custom class with yield
__iter__
with __next__
in custom class with yield
So, if you want to use yield
(in order to create a generator) with __iter__
and __next__
methods in a custom class
, do not put just the yield
into the __next__
method, but use it with __iter__(self)
and return self.__next__()
instead return self
.所以,如果你想在自定义
class
中使用yield
(为了创建生成器)和__iter__
和__next__
方法,不要只将yield
放入__next__
方法,而是将它与__iter__(self)
一起使用并return self.__next__()
而不是return self
。
Code:代码:
class T:
def __init__(self):
self.s = 10
def __iter__(self):
return self.__next__()
def __next__(self):
for i in range(self.s):
yield i
if __name__== '__main__':
obj = T()
for i in obj:
print(i)
Terminal:终端:
C:\...>python test.py
0
1
2
3
4
5
6
7
8
9
C:...>
Also, you can call from __iter__
any other method instead __next__()
.此外,您可以从
__iter__
调用任何其他方法而不是__next__()
。
Code:代码:
class T:
def __init__(self):
self.s = 10
def __iter__(self):
return self.foo()
def foo(self):
for i in range(self.s):
yield i
if __name__== '__main__':
obj = T()
for i in obj:
print(i)
You will have exactly the same results.您将得到完全相同的结果。
yield
in __iter__
method without __next__
method__next__
方法的情况下在__iter__
方法中yield
I don't think it is a good idea to use yield
in __iter__
.我认为在
__iter__
中使用yield
不是一个好主意。 Ok, it works, but I think that destroys the class API.好的,它有效,但我认为这会破坏 class API。
__iter__
with __next__
in custom class without yield
__iter__
with __next__
in custom class without yield
Use these methods ( __iter__
and __next__
).使用这些方法(
__iter__
和__next__
)。 In the __iter__
return self
and do not forget to raise StopIteration
in __next__
method.在
__iter__
return self
中,不要忘记在__next__
方法中raise StopIteration
。
Code:代码:
class T:
def __init__(self):
self.s = 10
def __iter__(self):
self.__i = -1
return self
def __next__(self):
while self.__i < self.s-1:
self.__i+=1
return self.__i
raise StopIteration
if __name__== '__main__':
obj = T()
for i in obj:
print(i)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.