2012-11-09 11 views
8

私は多くの場合、私はの線に沿ってコードで終わる、書式設定文字列の非常に多くを行ういくつかのコードを持っている:呼び出しスコープから変数を引き出すストリングフォーマッターは悪い習慣ですか?

私は大規模な文字列に多数の変数を補間しようとしている
"...".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}") 
+1

あなたもちょうど '地元()'や 'グローバル()' –

+0

@FC使用することができます確かに、しかし、 '補間( "..."、**地元の人々を持ちます()) 'は、どこも乱雑に見えますが、' s'がローカル変数であるまれなケースで失敗します。最初の引数を2回設定しようとしているので、 – Eric

+0

私は明らかな質問をします。これらの変数はなぜグローバルスコープにあり、最初は辞書にないのですか? – Wilduck

答えて

8

以下のコードは、より簡単で安全な方法です。 inspect.currentframeは、Pythonのすべての実装で利用できるわけではありません。 jython、ironpython、pypyでは、cpythonのように見えるので、利用できない可能性があります。これによりコードの可搬性が低下します。

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

この技術は、実際にはこれは 「**」表記でキーワード引数としてテーブルを渡すことによってもを行うことができるPython tutorial's Input and Output chapter

に記載されています。これは、 組み込みのvars()関数と組み合わせて使用​​すると特に便利です。この関数は、すべてのローカル変数 を含む辞書を返します。 inspect.currentframe

CPythonの実装の詳細はPythonのドキュメントでも

:この関数は 内のPythonのすべての実装が存在することが保証されていないインタプリタにPythonのスタック フレームサポートに依存しています。 Pythonスタックフレームサポートなしの実装で実行している場合、この関数はNoneを返します。 inspectモジュールで

2

currentframeは、このように定義されています

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

のでsys_getframe属性を持っていない限り、interpolate機能が動作しません。 sys._getframeため

The docs言う:

はCPythonの実装の詳細を:この機能は、 内部および専門的な目的のために使用する必要があります。 Pythonのすべての実装で が存在することは保証されていません。関数本体に

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

を書く


は、はるかに長い

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

以上でないと、あなたのコードは、よりポータブルになります。

3

古き良きMailmanは、まさにこのことを行う機能_あり:バリー・ワルシャワはそれを行うことができれば、なぜ我々することはできませんので、

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 

を?

4

更新:のPython 3.6は、この機能(より強力なバリアント)組み込みがあります。

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

はそれ[手動ソリューションは]ネストされた関数と、やや意外な行動につながるPEP 0498 -- Literal String Interpolation


を参照してください。 :

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)) 
関連する問題