Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

short form for string.format(...,**locals())

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?

like image 715
loopbackbee Avatar asked Oct 23 '13 18:10

loopbackbee


3 Answers

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.

like image 107
Corley Brigman Avatar answered Nov 17 '22 06:11

Corley Brigman


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")
like image 44
John Spong Avatar answered Nov 17 '22 06:11

John Spong


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.

like image 1
geekQ Avatar answered Nov 17 '22 06:11

geekQ