2016-01-27 44 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()事情與我有關。如果我在另一個想要使用的文件中的另一個範圍中定義一個函數?