Python:字符串格式中缺少参数(lazy eval)

时间:2012-03-31 12:08:04

标签: python string formatting

我正在尝试对我的字符串上的参数进行一些“post”/“lazy”评估。假设我是这样的:

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

我想要更换{table_name}替换的字符串,而不是{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 谢谢你的帮助!

6 个答案:

答案 0 :(得分:7)

您可以用自己替换条件:

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

给了我们:

SELECT * FROM users WHERE {condition}

您可以稍后使用此字符串填写条件。

答案 1 :(得分:6)

您不能使用format函数,因为它会引发KeyError。

string.Template支持安全替代:

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

'SELECT * FROM users WHERE $condition'

如果您使用普通变量名称(没有格式说明符,没有索引等等),这也会有效(感谢@Simeon Visser的想法):

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}'

答案 2 :(得分:1)

这建立在@Karoly Horvath的答案之上,以增加对指定键的索引键和属性访问的支持:

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}

答案 3 :(得分: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)

我没有做到这一点,但我认为这是一个很好的解决方案。一个缺点是您无法拨打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))

然后,您可以将safestr对象创建为a = safestr(mystr)(对于名为str的某些mystr,您实际上可以调用 mystr.safeformat(**kwargs)

e.g。

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)失败了。当然,使用safestr时,您可以投放safeformat

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

但这看起来并不理想。我希望Python只为str类提供某种safeformat方法。

答案 4 :(得分: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')

输出:

{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

答案 5 :(得分:0)

string.Template.safe_substitute的替代方法可以像这样继承string.Formatter

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'})

输出:

foo: {value}