Suggestions

Raymond Hettinger othello at javanet.com
Sat Jan 12 15:25:03 EST 2002


Suggestion #1:   Issue a dubious syntax warning for multiple
assignments where there are overlaps between the assigned-to
variables:

>>> a = ['cat','dog']
>>> i = 1
>>> i, a[i] = 0, 'boo'
>>> a
['boo', 'dog']        # not ['cat','boo'] which was expected

The problem was even less visible in my heapsort code
where the line:                 x, a[x], a[t] = t, a[t], a[x]
needed to be changed to:  a[x], a[t], x = a[t], a[x], t

The Python Reference Manual details what is going on here,
but I had to find that out the hard way.  This is an especially
difficult bug to find -- you can stare right at the problem and
not realize what happened.  Repeatedly using statements like
a,b=b,a will tend to create psychological expectations which
make it hard to see the bug in the above code.

----------------
Suggestion #2:  Add new built-in functions:  xmap, xfilter, and xzip.

The new generators and iterators make lazy evaluation the
norm in Python 2.2 except for the functional constructs.
There is no reason why they can't be lazy also -- generating
output only when needed, consuming inputs only when
needed , and allowing for potentially infinite input streams.
For example:

def xzip( *sequences ):
    gens = map(iter, sequences)
    while 1:
        yield tuple( [g.next() for g in gens] )

import sys, time
def timestamp():
    while 1:
        yield time.ctime()
for t, line in xzip( timestamp(), iter(sys.stdin.readline,'') ):
    print 'Console log:',  t, line


---------------
Suggestion #3:  Allow 'yield' with no return value.

This more closely parallels the behavior of 'return' and there
is no reason to disallow it.  I ran across this while modeling
the use of 'yield' in consumer code and .next() in the producer
code to create a lazy output stream:

def index( ):
    namegen = getindexnames()
    try:
        while 1:
            tgt = Image.new( 'RGB', indexSize, bgColor )
            for place in getplacements():
                yield None           # waits until thumb is avail
                if thumb == None: raise 'IndexDone'
                tgt.paste( thumb, place )
            tgt.save( namegen.next() )
    except 'IndexDone':
        tgt.save( namegen.next() )
        yield None

indexer = index()
n = index()
for thumb in getthumbs(sys.argv[1]):
    n.next()                             # Submit value to stream
thumb = None; n.next()         # Flush the stream


----------------
Suggestion #4:

Add a yield-like keyword 'accept' which suspends execution
just like 'yield' but creates an object with two methods
.submit() and .flush() which can take arguments and feed them
into 'accept'.  Flush returns and raises a StopStream exception.

'yield' suspends execution and sends back a value.
'accept' suspends execution and waits to take-in a value.
'yield' makes it easy to write lazy producers.
'accept' makes it easy to write steam-like, lazy consumers.

The code from suggestion #3 would simplify to:

def index():
    'Load thumbnails into a single index print'
    namegen = getindexnames()
    try:
        while 1:
            tgt = Image.new( 'RGB', indexSize, bgColor )
            for place in getplacements():
                accept thumb           # waits until thumb is avail
                tgt.paste( thumb, place )
            tgt.save( namegen.next() )
    except StopStream:
        tgt.save( namegen.next() )

n = index()
for thumb in getthumbs(sys.argv[1]):
    n.submit(thumb)      # Submit value to stream
n.flush()                     # Flush the stream


Comments:
-- index() is the consumer of thumbnail images and the
main code is the producer.  This is the reverse of the
normal situation with 'yield'
-- The calling sequence in the main section closely
parallels the use of a file output stream:
    f = file('outfil','wb')
    f.submit( data )
    f.submit( moredata )
    f.flush()
-- I can't think of an easy was to combine yield and accept
in the same function.
-- It would be nice to have a syntax that didn't require a
try: except StopStream to trigger the code for .flush()


Thanks for reading this far.
Now I'll be quiet until 2003,


Raymond Hettinger










More information about the Python-list mailing list