8

I usually use the following pattern (as mentioned in this question):

a=1
s= "{a}".format(**locals())

I think it's a great way to write easily readable code.

Sometimes it's useful to "chain" string formats, in order to "modularize" the creation of complex strings:

a="1"
b="2"
c="{a}+{b}".format(**locals())
d="{c} is a sum".format(**locals())
#d=="1+2 is a sum"

Pretty soon, the code is pestered with X.format(**locals()). To solve this problem, I tried to create a lambda:

f= lambda x: x.format(**locals())
a="1"
b="2"
c= f("{a}+{b}")
d= f("{c} is a sum")

but this throws a KeyError, since locals() are the lambda's locals.

I also tried to apply the format only on the last string:

a="1"
b="2"
c="{a}+{b}"
d="{c} is a sum".format(**locals())
#d=="{a}+{b} is a sum"

But this doesn't work, since python only formats once. Now, I could write a function that formats repeatedly until there's nothing more to do:

def my_format( string, vars ):
    f= string.format(**vars)
    return f if f==string else my_format(f, vars)

but I'm wondering: is there a better way to do this?

Community
  • 1
  • 1
loopbackbee
  • 21,962
  • 10
  • 62
  • 97

4 Answers4

4

f = lambda x, l=locals(): x.format(**l) appears to work...

and if you wanted a version that is a little more all-encompassing (and probably a lot slower):

fg = lambda x, l=locals(), g=globals(): x.format(**dict(g.items() + l.items()))

will find the symbols in either locals or globals.

Corley Brigman
  • 11,633
  • 5
  • 33
  • 40
  • You probably want locals to override globals. Currently, you have it the other way round, since later arguments to dict win. – Talia Apr 21 '15 at 18:27
2

If you only need to do this within the function scope as a local shortcut, the following will work:

def formatter(fmt, loc=locals()):
    return fmt.format(**loc)

However, this will bind the value returned by locals() at the time of function declaration, rather than execution, so it will not be updated as values change, nor will it be useful when called from any other scope.

If you want to get access to the calling method's locals, you need to inspect the call stack (http://docs.python.org/2/library/inspect.html)

import inspect

def formatter(fmt):
    parent = inspect.stack()[1][0] # 1 = the previous frame context
                                   # 0 = the frame object
    return fmt.format(**parent.f_locals)

Note that this may not work for implementations of python that are not CPython.

Now you can do:

a = "1"
b = "2"
c = formatter("{a}+{b}")
d = formatter("{c} is a sum")
John Spong
  • 1,361
  • 7
  • 8
  • actually, this is true sometimes, but not always... it looks like locals() actually returns a pointer to the dict value of 'locals', which seems to change. You can do the following experiments: `f = locals(); x = 1; g = locals(); f == g`, which will be True, even though you've modified a local value between f and g. Also: `def fn(x, l=locals()): print l[x]; x = 2; fn('x'); x = 3; fn('x')` will print 2, then 3. It seems like it should be true sometimes, so it's a good comment, but there seem to be a lot of practical cases where you can ignore it. – Corley Brigman Jul 01 '15 at 13:39
1

Starting with Python 3.6 the effect of **locals() is already included in string#format or rather "formatted string literals".

See also PEP 498 and Python 3.6 release notes.

geekQ
  • 29,027
  • 11
  • 62
  • 58
0

It's not a one-liner, but it works:

def fmt(s, l=locals()):
    while '{' in s:
        s = s.format(**l)
    return s

a="1"
b="2"
c="{a}+{b}"
d="{c} is a sum"

print fmt(d)  # 1+2 is a sum

Here's a one line (and slightly less efficient) recursive version:

fmt = lambda s, l=locals(): fmt(s.format(**l), l=l) if '{' in s else s
martineau
  • 119,623
  • 25
  • 170
  • 301