11

我有一些代码进行了大量的字符串格式化,通常,我最终得到的代码如下:

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

我试图将大量变量插入到一个大字符串中。

是否有充分的理由不编写这样一个使用inspect模块查找变量进行插值的函数?

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}")
4

4 回答 4

9

更新: Python 3.6 内置了这个特性(一个更强大的变体):

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

参见PEP 0498 -- 文字字符串插值


它[手动解决方案]导致嵌套函数的行为有些令人惊讶:

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()

注意:相同的调用成功或失败取决于变量是否在其上方或下方提及。

在哪里callerscope

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))
于 2012-11-09T17:57:16.883 回答
8

下面的代码是一种更简单、更安全的方法。inspect.currentframe 并非在所有 python 实现中都可用,因此您的代码在不可用时会中断。在 jython、ironpython 或 pypy 下它可能不可用,因为它似乎是一个 cpython 东西。这会降低您的代码的可移植性。

print "{x}, {y}".format(**vars())

这种技术实际上在Python 教程的输入和输出章节中有所描述

这也可以通过使用“**”符号将表作为关键字参数传递来完成。这与新的内置 vars() 函数结合使用特别有用,该函数返回包含所有局部变量的字典。

也在 python 文档中为inspect.currentframe

CPython 实现细节:此函数依赖于解释器中的 Python 堆栈帧支持,并不保证在所有 Python 实现中都存在。如果在不支持 Python 堆栈框架的实现中运行,则此函数返回 None。

于 2012-11-09T16:48:40.553 回答
3

好老邮递员有一个功能_可以做到这一点:

def _(s):
    if s == '':
        return s
    assert s
    # Do translation of the given string into the current language, and do
    # Ping-string interpolation into the resulting string.
    #
    # This lets you write something like:
    #
    #     now = time.ctime(time.time())
    #     print _('The current time is: %(now)s')
    #
    # and have it Just Work.  Note that the lookup order for keys in the
    # original string is 1) locals dictionary, 2) globals dictionary.
    #
    # First, get the frame of the caller
    frame = sys._getframe(1)
    # A `safe' dictionary is used so we won't get an exception if there's a
    # missing key in the dictionary.
    dict = SafeDict(frame.f_globals.copy())
    dict.update(frame.f_locals)
    # Translate the string, then interpolate into it.
    return _translation.gettext(s) % dict

所以如果巴里华沙可以做到这一点,我们为什么不能呢?

于 2012-11-09T17:27:53.583 回答
2

inspect模块中,currentframe定义如下:

if hasattr(sys, '_getframe'):
    currentframe = sys._getframe
else:
    currentframe = lambda _=None: None

因此,除非sys具有_getframe属性,否则该interpolate功能将不起作用。

文档sys._getframe说:

CPython 实现细节:此函数应仅用于内部和专用目的。它不保证在 Python 的所有实现中都存在。


写作

"{x}, {y}, {z}".format(**vars())

在函数体中并不比

interpolate("{x}, {y}, {z}")

并且您的代码将更具可移植性。

于 2012-11-09T16:57:47.807 回答