Python-3.2 coroutine: AttributeError: 'generator' object has no attribute 'next'

Question:

Quoting from Python Essential Reference, by David Beazley, page 20:

Normally, functions operate on a single set of input arguments. However, a function can also be written to operate as a task that processes a sequence of inputs sent to it. This type of function is known as a coroutine and is created by using the yield statement as an expression (yield) as shown in this example:

def print_matches(matchtext):
    print "Looking for", matchtext
    while True:
        line = (yield)       # Get a line of text
        if matchtext in line:
            print line

To use this function, you first call it, advance it to the first (yield), and then start sending data to it using send(). For example:

>>> matcher = print_matches("python")
>>> matcher.next() # Advance to the first (yield)
Looking for python
>>> matcher.send("Hello World")
>>> matcher.send("python is cool")
python is cool
>>> matcher.send("yow!")
>>> matcher.close() # Done with the matcher function call

Based on that information, I wrote this code:

#!/usr/bin/python3.2
import sys

def match_text(pattern):
    line = (yield)
    if pattern in line:
        print(line)

x = match_text('apple')
x.next()

for line in input('>>>> '):
    if x.send(line):
        print(line)

x.close()

But I get an error message like:

Traceback (most recent call last):
  File "xxx", line 9, in <module>
    matcher.next() # Advance to the first (yield)
AttributeError: 'generator' object has no attribute 'next'

Why doesn’t this code (or the code from the book, for that matter) work in Python 3.2? It seems that what is supposed to be a coroutine, is treated as a generator instead – why? What is going on here?

Asked By: user621819

||

Answers:

You’re getting thrown off by the error message; type-wise, Python doesn’t make a distinction – you can .send to anything that uses yield, even if it doesn’t do anything with the sent value internally.

In 3.x, there is no longer a .next method attached to these; instead, use the built-in free function next:

next(matcher)
Answered By: Karl Knechtel

In the case you find yourself patching somebody’s code, it seems that the built-in python3 next() function calls the iterator’s next() function, so you may be able to find/replace somebody’s python2 .next( with the python3-tolerable .__next__( as I just did to make portions of the primefac module work in python3 (among other trivial changes).

Here’s the reference:

next(iterator[, default])

Retrieve the next item from the iterator by calling its next()
method. If default is given, it is returned if the iterator is
exhausted, otherwise StopIteration is raised.

Answered By: sage

For python version 3.2 the syntax for the next() in-built function should be matcher.__next__() or next(matcher).

Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.