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() 関数と組み合わせると特に便利です。

inspect.currentframeのpythonドキュメントにもあります

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 に答える