41
#!/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()

This is a coroutine but Python3.2 sees it as a generator - why? What is going on here? I'm referring to Python Essential Reference by David Beazeley pg:20.

To quote the relevant section:

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

Why doesn't my code work - not that DB's works..

deathstar> python3.2 xxx   
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'
  • 4
    Duplicate of http://stackoverflow.com/questions/12274606/theres-no-next-function-in-a-yield-generator-in-python-3 – Brave Sir Robin Feb 07 '14 at 08:04
  • Possible duplicate of [there's no next() function in a yield generator in python 3](https://stackoverflow.com/questions/12274606/theres-no-next-function-in-a-yield-generator-in-python-3) – Thomas Ayling May 21 '18 at 10:34

3 Answers3

68

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)
Steven C. Howell
  • 14,502
  • 13
  • 69
  • 84
Karl Knechtel
  • 56,349
  • 8
  • 83
  • 124
  • Specifically, the protocol was changes so that Python expects a `.__next__` method, rather than `.next`, to implement the logic. (Iteration can also be implemented by making the `.__iter__` method be a generator function.) The free function `next` should be used regardless: by convention, every method name starting and ending with `__` is intended for a special, behind-the-scenes purpose, and should not be called directly from outside the class. This also works in 2.6 and 2.7, if for some reason you are forced to use the Python equivalent of Windows XP or 7, respectively. – Karl Knechtel May 30 '22 at 20:36
13

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

Adil B
  • 12,696
  • 10
  • 51
  • 65
6

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.

sage
  • 4,580
  • 2
  • 39
  • 47