using generators with format strings

Bengt Richter bokr at oz.net
Tue Jul 27 19:55:47 CEST 2004


On Wed, 21 Jul 2004 15:21:50 -0500, marduk <marduk at python.net> wrote:

>I have a weird request.
>
>I want to be able to say
>
>def myvalues():
>    while True:
>        # stuff that determines a new somevalue
>        yield somevalue
>
>x = "Hello, %s, this is a %s with %s and %s on top of %s" % myvalues()
>y = "Yes it's true that %s has way too many %s's" % myvalues()
>
>I was hoping that myvalues() would be iterated over, but instead the
>interpreter gives me a "TypeError: not enough arguments for format string"
>error.  I tried tuple(myvalues()) and I think that kinda works but of
>course myvalues goes into an infinite loop. myvalues will not know before
>hand how many times it will be called.
>
>Is there actually a simple way of doing this that I'm overlooking?
>

If you are willing to modify your format strings so they call on a
mapping (with ignored key '' in this case), you can supply a mapping that will do the job:

 >>> def myvalues():
 ...     values = 'one two three'.split() + [4,5,6]
 ...     while True:
 ...         # stuff that determines somevalue
 ...         for somevalue in values:
 ...             yield somevalue
 ...
 >>> mapping = type('',(),{'__getitem__':lambda s,k,g=myvalues().next:g()})()
 >>> x = "Hello, %s, this is a %s with %s and %s on top of %s".replace('%','%()') % mapping
 >>> y = "Yes it's true that %s has way too many %s's".replace('%','%()') % mapping
 >>> x
 'Hello, one, this is a two with three and 4 on top of 5'
 >>> y
 "Yes it's true that 6 has way too many one's"

You can obviously define mapping as an instance of a more conventionally defined class also,
and pass the generator to its constructor. Maybe even differentiate among multiple keyword-named
generators if you want to feed in several value streams and not ignore keys from the format.

You don't want to use .replace('%','%()') if your format string already has some '%(...) or
'%%' instances in it, of course.

Hm, maybe if '%s' without mapping names were interpreted as a mapping with an integer key
whose value was the position of the %s in the format, then a mapping could (if it had the
integer key in question, otherwise its __str__ method would be called) be used with ordinary
formats as well. E.g.,

'%s %04x' % mapping

would get mapping[0] and mapping[1] as the values to convert per the format. Note that
that's not equivalent to '%(0)s %(1)04x' which would use keys '0' and '1' instead of 0 and 1.
You could of course make a mapping that would ignore the keys for some purpose, as I did above.
Just a wild idea, don't take too seriously ;-)

Regards,
Bengt Richter



More information about the Python-list mailing list