Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is a string formatter that pulls variables from its calling scope bad practice?

Tags:

python

I have some code that does an awful lot of string formatting, Often, I end up with code along the lines of:

"...".format(x=x, y=y, z=z, foo=foo, ...)

Where I'm trying to interpolate a large number of variables into a large string.

Is there a good reason not to write a function like this that uses the inspect module to find variables to interpolate?

import inspect

def interpolate(s):
    return s.format(**inspect.currentframe().f_back.f_locals)

def generateTheString(x):
    y = foo(x)
    z = x + y
    # more calculations go here
    return interpolate("{x}, {y}, {z}")
like image 346
Eric Avatar asked Nov 09 '12 16:11

Eric


People also ask

What is the point of string formatting?

String formatting uses a process of string interpolation (variable substitution) to evaluate a string literal containing one or more placeholders, yielding a result in which the placeholders are replaced with their corresponding values.

Is string formatting important in Python?

Strings are one of the most used and essential data types in Python. With that said, proper text formatting makes code and data much easier to read and understand.

What will happen if we use format function in string?

In java, String format() method returns a formatted string using the given locale, specified format string, and arguments. We can concatenate the strings using this method and at the same time, we can format the output concatenated string. Parameter: The locale value to be applied on the format() method.


1 Answers

Update: Python 3.6 has this feature (a more powerful variant) builtin:

x, y, z = range(3)
print(f"{x} {y + z}")
# -> 0 3

See PEP 0498 -- Literal String Interpolation


It[manual solution] leads to somewhat surprising behaviour with nested functions:

from callerscope import format

def outer():
    def inner():
        nonlocal a
        try:
            print(format("{a} {b}"))
        except KeyError as e:
            assert e.args[0] == 'b'
        else:
            assert 0

    def inner_read_b():
        nonlocal a
        print(b) # read `b` from outer()
        try:
            print(format("{a} {b}"))
        except KeyError as e:
            assert 0
    a, b = "ab"
    inner()
    inner_read_b()

Note: the same call succeeds or fails depending on whether a variable is mentioned somewhere above or below it.

Where callerscope is:

import inspect
from collections import ChainMap
from string import Formatter

def format(format_string, *args, _format=Formatter().vformat, **kwargs):
    caller_locals = inspect.currentframe().f_back.f_locals
    return _format(format_string, args, ChainMap(kwargs, caller_locals))
like image 108
jfs Avatar answered Nov 15 '22 18:11

jfs