I frequently find myself using the following pattern for string formatting.
a = 3
b = 'foo'
c = dict(mykey='myval')
#prints a is 3, b is foo, mykey is myval
print('a is {a}, b is {b}, mykey is {c[mykey]}'.format(**vars()))
That is, I often have the values I need to print in the local namespace, represented by a call to vars(). As I look over my code, however, it seems awfully unpythonic to be constantly repeating the .format(**vars())
pattern.
I'd like to create a function that will capture this pattern. It would be something like the following.
# doesn't work
def lfmt(s):
"""
lfmt (local format) will format the string using variables
in the caller's local namespace.
"""
return s.format(**vars())
Except that by the time I'm in the lfmt
namespace, vars() is no longer what I want.
How can I write lfmt so that it executes vars() in the caller's namespace such that the following code would work as the example above?
print(lfmt('a is {a}, b is {b}, mykey is {c[mykey]}'))
Edit: In order for lfmt
to work when called from different namespaces, you'll need the inspect
module. Note, as the documentation warns, the inspect
module may not be suitable for production code since it may not work with all implementations of Python
import inspect
def lfmt(s):
caller = inspect.currentframe().f_back
return s.format(**caller.f_locals)
a = 3
b = 'foo'
c = dict(mykey='myval')
print(lfmt('a is {a}, b is {b}, mykey is {c[mykey]}'))
# a is 3, b is foo, mykey is myval
You have to inspect the variables from the calling frames.
This will get you started:
import inspect
import pprint
def lfmt(s):
for frame in inspect.getouterframes(inspect.currentframe()):
f = frame[0]
print pprint.pformat(f.f_locals)
return '???'
if __name__ == '__main__':
a = 10
b = 20
c = 30
lfmt('test')
Here you are:
import sys
def lfmt(s):
"""
lfmt (local format) will format the string using variables
in the caller's local namespace.
"""
if hasattr(sys, "tracebacklimit") and sys.tracebacklimit == 0:
raise Exception, "failfailfail"
try:
raise ZeroDivisionError
except ZeroDivisionError:
f = sys.exc_info()[2].tb_frame.f_back
return s.format(**f.f_locals)
a = 5
somestring = "text"
print lfmt("{a} {somestring}")
The fact that it works doesn't mean you should use it. This is what developers call "major hack", usually shipped with a comment "XXX fix me XXX".
Is it so bad to type ,vars
each time you call the function?
def lfmt(s,v):
"""
lfmt (local format) will format the string using variables
from the dict returned by calling v()"""
return s.format(**v())
print(lfmt('a is {a}, b is {b}, mykey is {c[mykey]}',vars))
You could also use sys
instead of inspect
, but I don't know if it has the same problem with different implementations as inspect
has.
import sys
def lfmt(s):
caller = sys._getframe(1)
return s.format(**caller.f_locals)
This is as far as I got: Python string interpolation implementation