如何在string.Template模板中为datetime对象应用自定义格式?
问题原因
string.Template的默认占位符语法不支持在占位符内部嵌入格式说明符。它只能识别简单的占位符名称(比如${dt}),而你写的${dt:%a, %d.%m.%Y at %H:%M}会被解析成一个包含冒号的无效占位符,这就是为什么会抛出ValueError: Invalid placeholder in string的原因。
下面给你两种实用的解决方案:
方案1:提前格式化datetime对象(最简单)
如果场景比较简单,你可以先把datetime对象格式化为字符串,再传入substitute方法:
from datetime import datetime as dt import string # 先格式化datetime current_time = dt.now() formatted_time = current_time.strftime("%a, %d.%m.%Y at %H:%M") # 用模板替换 print(string.Template("Current time: ${dt}").substitute({"dt": formatted_time}))
这种方法的优点是零额外复杂度,直接利用Python内置的strftime完成格式化,适合只需要一次格式化的场景。
方案2:自定义Template子类(更灵活)
如果你需要在模板中直接写格式说明符(就像你最初期望的那样),可以自定义string.Template的子类,扩展它的占位符语法,支持${变量名:格式字符串}的写法:
from datetime import datetime as dt import string import re class FormattedTemplate(string.Template): # 重写正则模式,允许占位符后跟随:和格式字符串 pattern = r""" \$(?: (?P<escaped>\$) | (?P<named>[_a-z][_a-z0-9]*) | {(?P<braced>[_a-z][_a-z0-9]*)(?::(?P<format>[^}]+))?} | (?P<invalid>) ) """ def substitute(self, mapping=None, **kwds): # 合并映射和关键字参数 mapping = dict(mapping or {}, **kwds) def replace_match(match): # 处理转义的$ if match.group('escaped'): return match.group('escaped') # 处理普通命名占位符(如$dt) if match.group('named'): return str(mapping[match.group('named')]) # 处理带格式的大括号占位符 braced_name = match.group('braced') if braced_name: value = mapping[braced_name] format_str = match.group('format') if format_str: # 对datetime对象用strftime,其他对象用format格式化 if hasattr(value, 'strftime'): return value.strftime(format_str) else: return format(value, format_str) return str(value) # 处理无效占位符 raise ValueError(f"Invalid placeholder: {match.group(0)}") return re.sub(self.pattern, replace_match, self.template) # 使用示例 print(FormattedTemplate("Current time: ${dt:%a, %d.%m.%Y at %H:%M}").substitute({"dt": dt.now()})) # 还能格式化其他类型,比如数字 print(FormattedTemplate("Pi: ${pi:.2f}").substitute({"pi": 3.1415926}))
这个自定义类不仅支持datetime的格式化,还能处理其他可格式化的类型(比如浮点数、整数),适合需要在模板中频繁使用自定义格式的场景。
内容的提问来源于stack exchange,提问作者Moritz 'e1mo' Fromm
相关产品推荐
相关产品推荐

