Python string.format with inline pipes



我正在为用户消息格式化很多字符串。一个可能看起来像这样:

def sms(**kwargs):
  return "Sorry {name}, but your payment was rejects. Please visit {url} and try again.".format(
    name=kwargs.get('name'),
    url=shorten_url(kwargs.get('url'))
  )

如果我不需要重新格式化任何关键字参数,我可以这样做,这很甜蜜:

def sms(**kwargs):
  return "Sorry {name}, but your payment was rejects. Please visit {url} and try again.".format(**kwargs)

所以我希望也许可以做这样的事情:

def sms(**kwargs):
  return "Sorry {name}, but your payment was rejects. Please visit {url|shorten_url} and try again.".format(**kwargs)

所以我可以使用管道内联格式化字符串。这似乎没什么大不了的,但我正在写很多这样的消息。

注意到python string.vformat函数,但我不确定这是否是我正在寻找的。有什么想法吗?

如果你子类string.Formatter,你实际上可以实现自定义转换函数。以下示例基于此帖子

import string
class Template(string.Formatter):
    def convert_field(self, value, conversion):
        if conversion == 'u': # has to be a single char
            return value[:3] # replace with your shorten_url function
        # otherwise call the default convert_field method
        return super(Template, self).convert_field(value, conversion)
print(Template().format('{url!u}', url='SOME LONG URL'))

输出SOM

另一种选择是在将 kwargs 传递给格式之前对其进行修改:

>>> def sms(**kwargs):
...     kwargs['shorturl'] = shorten_url(kwargs['url'])
...     print('test {shorturl}'.format(**kwargs))

编辑:

基于你想使用的事实 globals() ,你可以使用类似的东西

def bold(s):
  return "<strong>" + s + "</strong>"
def span(s):
  return "<span>" + s + "</span>"
class Template(string.Formatter):
    def get_field(self, name, args, kwargs):
        parts = name.split('|')
        # use first part as actual field name ('url' in this case)
        obj, used_key = super(Template, self).get_field(parts.pop(0), args, kwargs)
        for filter in parts:
            obj = globals()[filter](obj) # call remaining parts as filter functions
        return obj, used_key
print(Template().format('{url|bold|span}', url='SOME LONG URL'))
# Outputs: <span><strong>SOME LONG URL</strong></span>

|字符似乎与字段名称一起传递,因此您可以根据需要(ab)使用它。我建议添加一些错误处理,并检查函数的调用顺序是您所期望的。我也不确定使用 globals() 是一个好主意,特别是如果您要处理不安全的格式字符串。

管道

,或者更好的"过滤器",不是在Python stdlib模板中实现的。

标准 Python 库提供各种格式选项(对齐、填充、数字)格式),但它肯定有一些限制。

许多模板包确实支持自定义过滤器,其中之一是jinja2

from jinja2 import Environment

def dumb_shorten_url(url):
    # just shortening for fun, implement real shortening
    return url[6:]
env = Environment()
env.filters["shorten_url"] = dumb_shorten_url

templ = env.from_string("Sorry {{name}}, but your payment was rejects. "
                        "Please visit {{url|shorten_url}} and try again.")
kwargs = {"name": "James", "url": "http://acme.com/one/two"}
print templ.render(**kwargs)

jinja2提供了更多功能(从文件系统、目录、循环、条件表达式,转义 HTML...),但上面的例子应该证明,它适用于"管道"。

所以这更符合我正在寻找的内容:

import re
def bold(string):
  return "<strong>" + string + "</strong>"
def format(string, **kwargs):
  # using the global scope, we can pipe kwargs through functions!
  scope = globals()
  def replace(substr):
    pieces = substr.group()[1:-1].split("|")
    value = kwargs.get(pieces[0])
    if len(pieces) > 1:
      pipes = pieces[1:]
      for pipe in pipes:
        value = scope[pipe](value)
    return value
  return re.sub(r"{S+}", replace, string)
format("Hello {name|bold}, {yo}", **{"name":"Joe Schmo", "yo":"gimme more"})

它有效,但整个globals()的事情都与我有关。如果我在要使用的另一个文件中的另一个作用域中定义函数,该怎么办?

最新更新