2016-01-27 47 views
2

我为用户消息格式化了很多字符串。一个可能是这样的:Python string.format与内联管道

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

如果我不需要重新格式化所有的关键字args来,我可以做到这一点,这是甜蜜的:

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函数,但我不知道如果多数民众赞成我正在寻找。有任何想法吗?

+2

也许你可以使用真正的模板引擎,如[jinja2](http://jinja.pocoo.org/docs/dev/)? – univerio

+0

是的,我见过的很多Python模板系统都具有这样的功能。 jinja2以[自定义过滤器]的形式(http://jinja.pocoo.org/docs/dev/api/#custom-filters) – Marius

+0

是的,我实际上正在离开jinja,转而使用功能组合和字符串连接。 [这是一个片段](http://pastebin.com/DbNptvTQ)我写的一些实际代码 - 我更喜欢这种方式。 – Chet

回答

3

管道或更好的“过滤器”,没有在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 ...),但上面的例子应证明,它可与 “管道”配合使用。

+0

是的,我实际上正在远离金刚 - 我在另一个评论中提到过。还是)感谢你的建议! – Chet

+0

@Chet我有兴趣知道原因,你能提供一些链接到你的评论?我们使用jinja2通常对此非常满意。 –

+0

[我在这里讨论过](http://stackoverflow.com/questions/35026423/python-string-format-with-inline-pipes/35027016?noredirect=1#comment57782497_35026423) – Chet

4

如果您继承string.Formatter,您实际上可以实现自定义转换函数。下面的例子是基于this post

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()是一个好主意,特别是如果你要处理不安全的格式字符串。

+0

很高兴看到即使python stdlib也提供了自定义格式器。 –

+1

您也可以覆盖['format_field'](https://docs.python.org/2/library/string.html#string.Formatter.format_field)更复杂的场景,甚至可以扩展格式化语法,并允许多个转换标记的一个字符。 –

+0

嗯。因为有一个内置的“转换”使用'! '语法?我不知道。奇怪它只支持一个字母和一个转换。我希望能够访问命名空间中的所有功能。我忘了python让你添加对象方法。我不知道如何做到这一点,但我可以创建自己的'string.custrom_format'函数吗? – Chet

0

因此,这是一起什么,我一直在寻找的线条更加:

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()事情与我有关。如果我在另一个想要使用的文件中的另一个范围中定义一个函数?