2016-06-14 17 views
-1

フォーマット文字列の辞書が与えられた場合、 カスケード/再帰的文字列補間を行いたいと思います。Pythonのカスケード文字列補間

FOLDERS = dict(home="/home/user", 
       workspace="{home}/workspace", 
       app_project="{workspace}/{app_name}", 
       app_name="my_app") 

私はこの実装を開始しました:

def interpolate(attrs): 
    remain = [k for k, v in attrs.items() if "{" in v] 
    while remain: 
     for k in remain: 
      attrs[k] = attrs[k].format(**attrs) 
     remain = [k for k in remain if "{" in attrs[k]] 

interpolate()機能は、最初のフォーマット文字列を選択します。 次に、フォーマット文字列がなくなるまで文字列を置換します。

私は、次のPythonの辞書でこの関数を呼び出すと、私が手:

>>> import pprint 
>>> pprint.pprint(FOLDERS) 
{'app_name': 'my_app', 
'app_project': '/home/user/workspace/my_app', 
'home': '/home/user', 
'workspace': '/home/user/workspace'} 

結果はOKですが、この実装はリファレンス・サイクルを検出しません。

たとえば、次の呼び出しの結果、Infiniteループが発生します。

>>> interpolate({'home': '{home}'}) 

もっと良い実装をいただけますか?

EDIT:ソリューション

私はレオンのソリューションは、あまりにもセルジュBellestaの一つの良いとシンプルだと思います。

def interpolate(attrs): 
    remain = [k for k, v in attrs.items() if "{" in v] 
    while remain: 
     for k in remain: 
      attrs[k] = attrs[k].format(**attrs) 
      fmt = '{' + k + '}' 
      if fmt in attrs[k]: # check for reference cycles 
       raise ValueError("Reference cycle found for '{k}'!".format(k=k)) 
     remain = [k for k in remain if "{" in attrs[k]] 
+2

*「?誰が私より良い実装を与えることができます」* - それはSOのためではありません。あなたが解決しようとしている実際の問題は何ですか?実際には参照サイクルの入力がありますか? – jonrsharpe

+0

* "私にはより良い実装を提供できますか?" * - 指定された入力に対して全く同じ結果を生成するだけですか? –

+0

事実なら、私は一般的なソリューションを探しています。 exemplesはイラストレーションのためのものです。はい、フォルダを定義するユーザが - 典型的には設定ファイルにエラーを生じた場合、サイクルを繰り返すことができます: '' interpolate() ''関数がそれを見つけなければなりません。 –

答えて

1

あなたは簡単にループのために、このような参照サイクルを確認することができます。

私はそのように実装します。 forループ内の一致する値でキーが参照されているかどうかを確認してください:

def interpolate(attrs): 
    remain = [k for k, v in attrs.items() if "{" in v] 
    while remain: 
     for k in remain: 
      attrs[k] = attrs[k].format(**attrs) 
      if '{%s}' % k in attrs[k]: # check for reference cycles 
       raise ValueError("Input contains at least one reference cycle!") 
     remain = [k for k in remain if "{" in attrs[k]] 

ここで、参照サイクルがある場合、エラーが発生します。これにより、任意の長さの参照サイクルが検出され、すべての置換が完了するまで置換されます。

0

あなたの唯一の問題は無限ループを避けるために、循環参照を検出することである場合は、1つの補間がその入力を返すとすぐに停止することができます:

def interpolate(attrs): 
    remain = [k for k, v in attrs.items() if "{" in v] 
    while remain: 
     for k in remain: 
      attrs[k] = attrs[k].format(**attrs) 
     temp = [k for k in remain if "{" in attrs[k]] 
     if temp == remain: 
      # cyclic reference detected 
      ... 
     remain = temp