string interpolation (Was: Newbie can't figure out documentation practices)

Bengt Richter bokr at oz.net
Tue May 13 00:42:26 EDT 2003


On Mon, 12 May 2003 15:52:30 -0600, Fernando Perez <fperez528 at yahoo.com> wrote:

>Antonios Christofides wrote:
>
>> Fernando Perez wrote:
>>>  The only other area where you're likely to cry a bit is proper string
>>>  interpolation, which sucks in python.
>> 
>> You could have been in my mind. My second thread to this group would be
>> that the only thing I'm missing badly from Perl is string interpolation.
>> So I asked only one of my two -unrelated- questions and got answers to
>> both :-)
>
>And as you already saw from the ensuing thread, my view is in the definite
>minority around here ;)  But my guess wasn't so much due to my amazing
>telepathic abilities, but rather to the fact that you're following the
>necessary thought process of a successful perl-to-python conversion.  So
>guessing your next moves isn't too difficult at this point :)
>
>Soon you will discover the joys of a clean module system where every file is
>automatically an importable library, of data structures which allow you to
>build complex, nested things (e.g. lists of dicts of dicts)  with zero pain, or
>an object-oriented syntax so trivial that you'll use classes for organizing
>data cleanly in many situations.  After that, you'll start wondering why you
>needed the Perl cookbook every time you needed a hash of hashes, and couldn't
>quite get all the $'s in the right places for all of the dereferences to work
>correctly.  Welcome to python!
>
>Note that Sismex's final solution, reproduced again here for convenience's sake,
>is a fairly clean approach for the string interpolation issue.  It still
>requires the somewhat ugly-looking '%(stuff)modifier' python syntax, but that
>syntax has the advantage of allowing very fine control over the output.  So in
>the long run it does buy you something new you didn't have in perl :)  Just
>keep it around in some 'utils' module you always load, and you won't worry
>about the issue again.
>
>Cheers,
>
>f.
>
>##################################
>class EvalDict:
>    """
>    Emulate a dict which evaluates its contents in the caller's frame.
>
>    Usage:
>    >>>number = 19
>    >>>text = "python"
>    >>>print "%(text.capitalize())s %(number/9.0).1f rules!" % EvalDict()
>    """
>
>    # This version is due to sismex01 at hebmex.com on c.l.py, and is basically a
>    # modified (shorter) version of:
>    # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/66018 by
>    # Skip Montanaro (skip at pobox.com).
>
>    def __getitem__(self, name):
>        frame = sys._getframe(1)
>        return eval(name, frame.f_globals, frame.f_locals)
>
>
I think I would prefer not to use eval, e.g.,:

 >>> class GetVar(object):
 ...     import sys
 ...     def __getitem__(self, name):
 ...         frame = self.sys._getframe(1)
 ...         return frame.f_locals.get(name, frame.f_globals.get(name,'<%s undef>'%name))
 ...
 >>> x=123
 >>> print 'Hello %(x)s, %(y)s!!' % GetVar()
 Hello 123, <y undef>!!
 >>> y=456
 >>> print 'Hello %(x)s, %(y)s!!' % GetVar()
 Hello 123, 456!!

It's not that efficient though, since the default expressions get evaluated from the inside.
Better would be:

      
class GetVar(object):
    import sys
    def __getitem__(self, name):
        frame = self.sys._getframe(1)
        try:
            return frame.f_locals[name]
        except KeyError:
            return frame.f_globals[name]

Of course this will raise KeyError if name is undefined, but you can easily make it
equivalent to the other. Hm... there probably is some thinking to do to make sure
one can't create a rat's nest tangle of frames that refuse to die, but I'm too hungry ;-/

BTW, There seems to be some problem with using either one (or the eval version) in a nested
context, where a symbol is bound in a scope enclosing the local, but not global. The
global is not shadowed unless there is an explicit reference in the code, e.g., print x
if x were the nested binding. I guess it's the old generate-global-read-by-default code
generation biting. A bit of a wart IMO... but how do you get efficient global access
otherwise? Low level cached weak ref? Does 2.3 deal with this?

Regards,
Bengt Richter




More information about the Python-list mailing list