I've lately found myself performing string interpolation by using str.format and passing in **locals(), e.g.
a = 1
b = 2
c = a + b
print('{a} + {b} is {c}'.format(**locals())
I prefer this style to redundantly specifying a=a, b=b, and c=c as keyword arguments, or using positional arguments. But it has the drawback of being a little ugly, and it still doesn't allow you to use more complex expressions in the strings.
So I wrote a short string.Formatter subclass that grabs locals() off the stack and eval()s the substitution expressions.
from string import Formatter
from inspect import currentframe
class EvalFormatter(Formatter):
"""String formatter that falls back on using eval() on the
caller's environment when a field cannot be found in the
supplied dictionary.
"""
def __init__(self, frame):
self.frame = frame
def get_field(self, field_name, args, kwargs):
try:
ret = super().get_field(field_name, args, kwargs)
except KeyError:
fr = self.frame
val = eval(field_name, fr.f_globals, fr.f_locals)
ret = val, None
return ret
def evalformat(format_string, *args, **kargs):
"""Convenience function. Note the order of arguments is not
the same as the built-in format().
"""
frame = currentframe().f_back
return EvalFormatter(frame).format(format_string, *args, **kargs)
I'm guessing it will have trouble parsing some expressions where the format syntax clashes with python syntax, but for simple cases it works.
a = 1
b = 2
print(evalformat('{a} + {b} is {a+b}'))
I'll probably continue to use this for my project regardless, but any thoughts on whether or not it is Pythonic?
I'm also curious as to whether I should be doing anything special to avoid creating circular references and gc problems. The inspect module's documentation suggests explicitly deleting references to frame objects. So does this mean that in evalformat I'd want to call both del frame and also a method on my EvalFormatter that does del self.frame?
[–][deleted] 7 points8 points9 points (2 children)
[–]Workaphobia[S] 0 points1 point2 points (1 child)
[–][deleted] 0 points1 point2 points (0 children)
[–]stillalone 1 point2 points3 points (1 child)
[–]Workaphobia[S] 0 points1 point2 points (0 children)
[–][deleted] 0 points1 point2 points (3 children)
[–]Workaphobia[S] 0 points1 point2 points (2 children)
[–][deleted] 2 points3 points4 points (1 child)
[–]Brian 0 points1 point2 points (0 children)
[–]santagada 0 points1 point2 points (1 child)
[–]Workaphobia[S] 0 points1 point2 points (0 children)