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

时间:2021-07-26 11:21:17

i'm trying to do some "post"/"lazy" evaluation of arguments on my strings. Suppose i've this:

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

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

I'd like to return the string with the {table_name} replaced, but not the {condition}, so, something like this:

我想返回替换{table_name}的字符串,而不是{condition},所以,像这样:

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

So, I can build the hole string later, like:

所以,我可以在以后构建孔字符串,如:

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

The result should be, of course:

结果当然应该是:

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

I've found this previous answer, this is exactly what I need, but i'd like to use the format string function.

我已经找到了之前的答案,这正是我需要的,但我想使用格式字符串函数。

python, format string Thank you for your help!

python,格式字符串谢谢你的帮助!

6 个解决方案

#1


6  

You can't use the format function because it will raise a KeyError.

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

string.Template supports safe substituion:

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'

If you use plain variable names (no format specifiers, no indexing, etc..) this will also work (thanks @Simeon Visser for the idea):

如果您使用普通变量名称(没有格式说明符,没有索引等等),这也将起作用(感谢@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


7  

You can replace the condition with itself:

您可以用自己替换条件:

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

which gives us:

这给了我们:

SELECT * FROM users WHERE {condition}

You can use this string later to fill in the condition.

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

#3


1  

This builds on @Karoly Horvath's answer to add support for index keys and attribute access on named keys:

这建立在@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

So to test it out:

所以要测试一下:

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)

Output:

输出:

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

#4


1  

I have been using this function for some time now, which casts the Dict of inputted keyword arguments as a SafeDict object that subclasses Dict.

我一直在使用这个函数,它将输入的关键字参数的Dict转换为子类Dict的SafeDict对象。

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

I didn't make this up, but I think it's a good solution. The one downside is that you can't call mystring.safeformat(**kwargs) - of course, you have to call safeformat(mystring,**kwargs).

我没有提出这个问题,但我认为这是一个很好的解决方案。一个缺点是你不能调用mystring.safeformat(** kwargs) - 当然,你必须调用safeformat(mystring,** kwargs)。


If you're really interested in being able to call mystr.safeformat(**kwargs) (which I am interested in doing!), consider using this:

如果你真的有兴趣能够调用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))

You can then create a safestr object as a = safestr(mystr) (for some str called mystr), and you can in fact call mystr.safeformat(**kwargs).

然后你可以创建一个safestr对象作为a = safestr(mystr)(对于一些名为mystr的str),你实际上可以调用mystr.safeformat(** kwargs)。

e.g.

例如

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

prints

版画

Hey, Bill. I am {myname}.

嘿,比尔。我是{myname}。

This is cool in some ways - you can pass around a partially-formatted safestr, and could call safeformat in different contexts. I especially like to call mystr.format(**locals()) to format with the appropriate namespace variables; the safeformat method is especially useful in this case, because I don't always carefully looks through my namespace.

这在某些方面很酷 - 您可以传递部分格式化的safestr,并可以在不同的上下文中调用safeformat。我特别喜欢调用mystr.format(** locals())来使用适当的命名空间变量进行格式化;在这种情况下,safeformat方法特别有用,因为我并不总是仔细查看我的命名空间。

The main issue with this is that inherited methods from str return a str object, not a safestr. So mysafestr.lower().safeformat(**kwargs) fails. Of course you could cast as a safestr when using safeformat:

这个问题的主要问题是str的继承方法返回一个str对象,而不是一个safestr。所以mysafestr.lower()。safeformat(** kwargs)失败了。当然,使用safeformat时可以将其作为safestr进行转换:

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

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

but that's less than ideal looking. I wish Python just gave the str class a safeformat method of some kind.

但那看起来并不理想。我希望Python只给str类一个安全格式的方法。

#5


0  

This is a slight change to @ShawnFumo's answer which has a small bug. We need to add a word boundary check (the \b in the regular expression) to ensure that we are matching only the failing key and another key that starts with the same string. This prevents a missing {foo} key from also treating {food} and {foolish} as if they were missing.

这是对@ 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

So to test it out:

所以要测试一下:

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

Output:

输出:

{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

#6


0  

An alternative to string.Template.safe_substitute could be subclassing string.Formatter like so:

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

Output:

输出:

foo: {value}

#1


6  

You can't use the format function because it will raise a KeyError.

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

string.Template supports safe substituion:

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'

If you use plain variable names (no format specifiers, no indexing, etc..) this will also work (thanks @Simeon Visser for the idea):

如果您使用普通变量名称(没有格式说明符,没有索引等等),这也将起作用(感谢@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


7  

You can replace the condition with itself:

您可以用自己替换条件:

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

which gives us:

这给了我们:

SELECT * FROM users WHERE {condition}

You can use this string later to fill in the condition.

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

#3


1  

This builds on @Karoly Horvath's answer to add support for index keys and attribute access on named keys:

这建立在@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

So to test it out:

所以要测试一下:

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)

Output:

输出:

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

#4


1  

I have been using this function for some time now, which casts the Dict of inputted keyword arguments as a SafeDict object that subclasses Dict.

我一直在使用这个函数,它将输入的关键字参数的Dict转换为子类Dict的SafeDict对象。

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

I didn't make this up, but I think it's a good solution. The one downside is that you can't call mystring.safeformat(**kwargs) - of course, you have to call safeformat(mystring,**kwargs).

我没有提出这个问题,但我认为这是一个很好的解决方案。一个缺点是你不能调用mystring.safeformat(** kwargs) - 当然,你必须调用safeformat(mystring,** kwargs)。


If you're really interested in being able to call mystr.safeformat(**kwargs) (which I am interested in doing!), consider using this:

如果你真的有兴趣能够调用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))

You can then create a safestr object as a = safestr(mystr) (for some str called mystr), and you can in fact call mystr.safeformat(**kwargs).

然后你可以创建一个safestr对象作为a = safestr(mystr)(对于一些名为mystr的str),你实际上可以调用mystr.safeformat(** kwargs)。

e.g.

例如

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

prints

版画

Hey, Bill. I am {myname}.

嘿,比尔。我是{myname}。

This is cool in some ways - you can pass around a partially-formatted safestr, and could call safeformat in different contexts. I especially like to call mystr.format(**locals()) to format with the appropriate namespace variables; the safeformat method is especially useful in this case, because I don't always carefully looks through my namespace.

这在某些方面很酷 - 您可以传递部分格式化的safestr,并可以在不同的上下文中调用safeformat。我特别喜欢调用mystr.format(** locals())来使用适当的命名空间变量进行格式化;在这种情况下,safeformat方法特别有用,因为我并不总是仔细查看我的命名空间。

The main issue with this is that inherited methods from str return a str object, not a safestr. So mysafestr.lower().safeformat(**kwargs) fails. Of course you could cast as a safestr when using safeformat:

这个问题的主要问题是str的继承方法返回一个str对象,而不是一个safestr。所以mysafestr.lower()。safeformat(** kwargs)失败了。当然,使用safeformat时可以将其作为safestr进行转换:

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

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

but that's less than ideal looking. I wish Python just gave the str class a safeformat method of some kind.

但那看起来并不理想。我希望Python只给str类一个安全格式的方法。

#5


0  

This is a slight change to @ShawnFumo's answer which has a small bug. We need to add a word boundary check (the \b in the regular expression) to ensure that we are matching only the failing key and another key that starts with the same string. This prevents a missing {foo} key from also treating {food} and {foolish} as if they were missing.

这是对@ 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

So to test it out:

所以要测试一下:

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

Output:

输出:

{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

#6


0  

An alternative to string.Template.safe_substitute could be subclassing string.Formatter like so:

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

Output:

输出:

foo: {value}