2012-03-31 20 views
3

私の文字列の引数の "post"/"lazy"評価をしようとしています。私が交換{table_name}で文字列を返すのが好きではなく{condition}ので、このようなものだろうPython:文字列書式(怠惰な評価)の引数がありません

s = "SELECT * FROM {table_name} WHERE {condition}" 

:私はこれをしたと仮定し

s1 = s.format(table_name = "users") 

だから、私が構築することができます穴列後に、のように:

final = s1.format(condition= "user.id = {id}".format(id=2)) 

結果はもちろん、次のようになります。

"SELECT * FROM users WHERE user.id = 2" 

私はこれまでの答えを見つけました。これは私が必要とするものですが、format文字列関数を使用したいと考えています。

python, format string ありがとうございました!

+1

もちろん、SQLインジェクション攻撃を防ぐために、置換文字列をすべてエスケープしようとしているのでしょうか? ;) –

+1

あなたの* * *ユーザーの入力がこれに入ることを*これまでには望んでいないのですか?この種のコードは、セキュリティホールを要求しています。 –

+0

もちろん、私はそれをやることはしません。おそらくそれは最良の例ではありませんでしたが、最初に私の心に来たものでした。 – santiagobasulto

答えて

6

フォーマット機能を使用できません。これはKeyErrorを発生させるためです。

string.Templateは安全substituionをサポートしています。

from string import Template 
s = Template('SELECT * FROM $table_name WHERE $condition') 
s.safe_substitute(table_name='users') 

'SELECT * FROM users WHERE $condition' 

あなたはプレーンな変数名(なし書式指定子、ノーインデックス、などを。)使用している場合、これはまた、(アイデアをありがとう@Simeonフィッセル)が動作します:

これは@Karoly Horvathのの答えに基づいています
def myformat(s, *args, **kwargs): 
    while True: 
    try: 
     return s.format(*args, **kwargs) 
    except KeyError as e: 
     e=e.args[0] 
     kwargs[e] = "{%s}" % e 

s = "SELECT * FROM {table_name} WHERE {condition}" 
myformat(s, table_name="users") 

'SELECT * FROM users WHERE {condition}' 
6

あなたは自分自身との条件を置き換えることができます。

s.format(table_name='users', condition='{condition}') 

私たちに与える:

SELECT * FROM users WHERE {condition} 

あなたは条件を満たすために、後でこの文字列を使用することができます。

+0

うわー、これはとても簡単です。私はこの作品を決して考えていないだろう。 – santiagobasulto

+0

それ自体と何かを置き換えることは、少なくとも私の理解からは非常にPythonのように見える... – jamylak

+0

まあ、それは短い、明確で、それは私にPythonの作品です:) –

1

は、インデックスキーのサポートを追加し、名前のキーにアクセス属性します

import re 

def my_format(template, *args, **kwargs): 
    next_index = len(args) 
    while True: 
    try: 
     return template.format(*args, **kwargs) 
    except KeyError as e: 
     key = e.args[0] 
     finder = '\{' + key + '.*?\}' 
     template = re.sub(finder, '{\g<0>}', template) 
    except IndexError as e: 
     args = args + ('{' + str(next_index) + '}',) 
     next_index += 1 

だから、それをテストするには:

class MyObj: 
    bar = 'baz' 

    def __repr__(self): 
    return '<MyObj instance>' 

my_obj = MyObj() 

template = '{0}, {1}, {foo}, {foo.bar}, {0}, {10}, {missing}' 
print my_format(template) 
print my_format(template, '1st', '2nd', missing='Not Missing') 
print my_format(template, foo=my_obj) 

出力:

{0}, {1}, {foo}, {foo.bar}, {0}, {10}, {missing} 
1st, 2nd, {foo}, {foo.bar}, 1st, {10}, Not Missing 
{0}, {1}, <MyObj instance>, baz, {0}, {10}, {missing} 
0

これは、小さなバグを持っている@ ShawnFumoの答えに若干の変更があります。失敗したキーと同じ文字列で始まる別のキーだけが一致するように、単語境界チェック(正規表現では\ b)を追加する必要があります。これにより、{foo}キーが{food}と{foolish}が欠けているかのように扱われるのを防ぐことができます。

import re 

def my_format(template, *args, **kwargs): 
    next_index = len(args) 
    while True: 
    try: 
     return template.format(*args, **kwargs) 
    except KeyError as e: 
     key = e.args[0] 
     finder = r'\{' + key + r'\b.*?\}' 
     template = re.sub(finder, r'{\g<0>}', template) 
    except IndexError as e: 
     args = args + ('{' + str(next_index) + '}',) 
     next_index += 1 

だから、それをテストするには:

class MyObj: 
    bar = 'baz' 

    def __repr__(self): 
    return '<MyObj instance>' 

my_obj = MyObj() 

template = '{0}, {1}, {foo}, {foo.bar}, {0}, {10}, {missing}' 
print my_format(template) 
print my_format(template, '1st', '2nd', missing='Not Missing') 
print my_format(template, foo=my_obj) 

print 

template2 = '{foo} and {food}' 
print my_format(template2) 
print my_format(template2, food='burger') 
print my_format(template2, foo=my_obj, food='burger') 

出力:

class LazyFormatter(string.Formatter): 
    def get_value(self, key, args, kwargs): 
     '''Overrides string.Formatter.get_value''' 
     if isinstance(key, (int, long)): 
      return args[key] 
     else: 
      return kwargs.get(key, '{{{0}}}'.format(key)) 

lazyfmt = LazyFormatter() 
print lazyfmt.format("{field}: {value}", **{'field': 'foo'}) 

出力:012 string.Template.safe_substituteに代わるものはそうのようstring.Formatterをサブクラス化することができ

{0}, {1}, {foo}, {foo.bar}, {0}, {10}, {missing} 
1st, 2nd, {foo}, {foo.bar}, 1st, {10}, Not Missing 
{0}, {1}, <MyObj instance>, baz, {0}, {10}, {missing} 

{foo} and {food} 
{foo} and burger 
repr(<MyObj instance>) and burger 
1

私はDictをサブクラスSafeDict対象として入力されたキーワード引数のDictをキャストしている、今いくつかの時間のために、この機能を使用しています。

def safeformat(str, **kwargs): 
     class SafeDict(dict): 
       def __missing__(self, key): 
        return '{' + key + '}' 
     replacements = SafeDict(**kwargs) 
     return str.format_map(replacements) 

私はこれを作成しませんでしたが、良い解決策だと思います。 1つの欠点は、mystring.safeformat(**kwargs)に電話できないことです。もちろん、safeformat(mystring,**kwargs)に電話する必要があります。


あなたはmystr.safeformat(**kwargs)呼び出すことができることで、本当に興味があれば、これを使用することを検討してください(私はやってに興味があります!):

class safestr(str): 
    def safeformat(self, **kwargs): 
     class SafeDict(dict): 
       def __missing__(self, key): 
         return '{' + key + '}' 
     replacements = SafeDict(**kwargs) 
     return safestr(self.format_map(replacements)) 

あなたはその後、a = safestr(mystr)としてsafestrオブジェクトを作成することができます( strmystrとなります)、実際には mystr.safeformat(**kwargs)と電話することができます。

mysafestr = safestr('Hey, {friendname}. I am {myname}.') 
print(mysafestr.safeformat(friendname='Bill')) 

プリント

Hey, Bill. I am {myname}.

これは、いくつかの方法でクールです - あなたは、部分的にフォーマットされたsafestrの周りに渡すことができ、かつ異なるコンテキストでsafeformatを呼び出すことができます。私は特にmystr.format(**locals())と呼んで、適切な名前空間変数でフォーマットするのが好きです。 safeformatメソッドは、この場合に特に便利です。なぜなら、私の名前空間を常に注意深く見ているわけではないからです。

この主な問題は、継承されたメソッドstrからstrオブジェクトを返し、safestrではないことです。だからmysafestr.lower().safeformat(**kwargs)が失敗します。もちろんsafeformatを使用するときは、safestrとしてキャストできます

safestr(mysafestr.lower()).safeformat(**kwargs)を、

をそれは理想的な探し未満です。私はPythonがちょうどstrクラスに何らかの種類のsafeformatメソッドを与えることを望みます。

関連する問題