[Python-Dev] PEP 342: simple example, closure alternative

Ian Bicking ianb at colorstudy.com
Thu Aug 25 21:10:43 CEST 2005


I was trying to translate a pattern that uses closures in a language 
like Scheme (where closed values can be written to) to generators using 
PEP 342, but I'm not clear exactly how it works; the examples in the PEP 
have different motivations.  Since I can't actually run these examples, 
perhaps someone could confirm or debug these:

A closure based accumulator (using Scheme):

(define (accum n)
  (lambda (incr)
   (set! n (+ n incr))
   n))
(define s (accum 0))
(s 1) ; -> 1 == 0+1
(s 5) ; -> 6 == 1+5

So I thought the generator version might look like:

def accum(n):
     while 1:
         incr = (yield n) or 0
         n += incr

 >>> s = accum(0)
 >>> s.next()
 >>> s.send(1)
0
 >>> s.send(5)
1
 >>> s.send(1)
6

Is the order of the output correct?  Is there a better way to write 
accum, that makes it feel more like the closure-based version?

Is this for loop correct?

 >>> s = accum(0)
 >>> for i in s:
...     if i >= 10: break
...     print i,
...     assert s.send(2) == i
0 2 4 6 8

Hmm... maybe this would make it feel more closure-like:

def closure_like(func):
     def replacement(*args, **kw):
         return ClosureLike(func(*args, **kw))
     return replacement

class ClosureLike(object):
     def __init__(self, iterator):
         self.iterator = iterator
         # I think this initial .next() is required, but I'm
         # very confused on this point:
         assert self.iterator.next() is None
     def __call__(self, input):
         assert self.iterator.send(input) is None
         return self.iterator.next()

@closure_like
def accum(n):
     while 1:
         # yields should always be in pairs, the first yield is input
         # and the second yield is output.
         incr = (yield) # this line is equivalent to (lambda (incr)...
         n += incr      # equivalent to (set! ...)
         yield n        # equivalent to n; this yield always returns None

 >>> s = accum(0)
 >>> s(1)
1
 >>> s(5)
6

Everything before the first (yield) is equivalent to the closed values 
between "(define (accum n)" and "(lambda" (for this example there's 
nothing there; I guess a more interesting example would have closed 
variables that were written to that were not function parameters).

-- 
Ian Bicking  /  ianb at colorstudy.com  /  http://blog.ianbicking.org


More information about the Python-Dev mailing list