[Python-ideas] Cofunctions - Back to Basics
Arnaud Delobelle
arnodel at gmail.com
Thu Oct 27 13:45:30 CEST 2011
On 27 October 2011 03:09, Greg Ewing <greg.ewing at canterbury.ac.nz> wrote:
> Still feeling in a peppish mood after the last round of PEP 335,
> I decided to revisit my cofunctions proposal. Last time round, I
> allowed myself to be excessively swayed by popular opinion, and
> ended up with something that lacked elegance and failed to address
> the original issues.
>
> So, I've gone right back to my initial idea. The current version
> of the draft PEP can be found here:
>
> http://www.cosc.canterbury.ac.nz/greg.ewing/python/generators/cd_current/cofunction-pep-rev5.txt
>
> together with updated examples here:
>
> http://www.cosc.canterbury.ac.nz/greg.ewing/python/generators/cd_current/Examples/
>
> and prototype implementation available from:
>
> http://www.cosc.canterbury.ac.nz/greg.ewing/python/generators/cofunctions.html
>
> In a nutshell, the 'cocall' syntax is gone. Inside a cofunction, if
> an object being called implements __cocall__, then a cocall (implicit
> yield-from) is performed automatically, otherwise a normal call is
> made.
Hi, I've taken the liberty to translate your examples using a small
utility that I wrote some time ago and modified to mimic your proposal
(imperfectly, of course, since this runs on unpatched python). FWIW,
you can see and download the resulting files here (.html files for
viewing, .py files for downloading):
http://www.marooned.org.uk/~arno/cofunctions/
These don't need ``yield from`` and will work both in Python 2 and 3.
The functionality is implemented with three functions defined in
cofunctions.py: cofunction, costart, coreturn.
The only changes that made to your examples are the following:
1. Add the line ``from cofunctions import *`` at the start of the file
2. Change
codef f(x, y): ...
to
@cofunction
def f(cocall, x, y): ...
3. Change 'cocalls' to cofunctions from within cofunctions as follows:
f(x, y)
becomes:
yield cocall(f, x, y)
4. Change return statements within cofunctions to as follows:
return 42
becomes
coreturn(42)
5. The 'value' attribute of StopIteration exceptions raised by
returning cofunctions becomes a 'covalue' attribute
Note that #1, #2 and #4 a trivial changes. To perform #3 is actually
simple because trying to call a cofunction raises and exception
pointing out the incorrect call, so running the examples show which
calls to change to cocalls. I could have avoided #5 but somehow I
decided to stick with 'covalue' :)
Here is, for example, the 'parser.py' example translated:
---------------------------- parser.py --------------------------
from cofunctions import *
import re, sys
pat = re.compile(r"(\S+)|(<[^>]*>)")
text = "<foo> This is a <b> foo file </b> you know. </foo>"
def run():
parser = costart(parse_items)
next(parser)
try:
for m in pat.finditer(text):
token = m.group(0)
print("Feeding:", repr(token))
parser.send(token)
parser.send(None) # to signal EOF
except StopIteration as e:
tree = e.covalue
print(tree)
@cofunction
def parse_elem(cocall, opening_tag):
name = opening_tag[1:-1]
closing_tag = "</%s>" % name
items = yield cocall(parse_items, closing_tag)
coreturn((name, items))
@cofunction
def parse_items(cocall, closing_tag = None):
elems = []
while 1:
token = yield
if not token:
break # EOF
if is_opening_tag(token):
elems.append((yield cocall(parse_elem, token)))
elif token == closing_tag:
break
else:
elems.append(token)
coreturn(elems)
def is_opening_tag(token):
return token.startswith("<") and not token.startswith("</")
run()
--------------------------- /parser.py --------------------------
--
Arnaud
More information about the Python-ideas
mailing list