Python格式化字符串时如何忽略KeyError并保留含属性的占位符
Python格式化字符串时如何忽略KeyError并保留含属性的占位符
看起来你想要的是在字符串格式化时,无论占位符是顶层键(如{name})还是带多层属性/索引的(如{name.capitalized}或{user['age']}),只要任意一层不存在,就返回原占位符而非抛出错误——这个需求对于本地化场景确实很实用,能避免用户因为 typo 或配置错误看到崩溃信息。
你之前的方案问题在于:
- 用
Default(dict)的__missing__只能处理顶层键的缺失,当返回{name}字符串后,格式化器会尝试访问它的capitalized属性,自然触发AttributeError; - 自定义
CustomFormatter的get_value只拦截了顶层键的KeyError,但没处理属性访问失败的情况,同样会因为字符串没有对应属性报错。
要解决这个问题,我们需要拦截整个占位符的解析过程,而不只是顶层键的获取。核心思路是:当任意一层的键/属性不存在时,返回一个“代理对象”,这个对象可以继续接受后续的属性/索引访问,最终被转为字符串时,返回完整的原占位符。
具体实现代码
首先定义一个PlaceholderProxy类,它负责跟踪当前占位符的完整路径,并在需要转为字符串时返回带大括号的原占位符:
class PlaceholderProxy: def __init__(self, path: str): self.path = path def __getattr__(self, attr: str) -> "PlaceholderProxy": # 处理属性访问,比如 {name.capitalized} 中的 .capitalized return PlaceholderProxy(f"{self.path}.{attr}") def __getitem__(self, key) -> "PlaceholderProxy": # 处理字典索引访问,比如 {user['age']} 中的 ['age'] if isinstance(key, str): return PlaceholderProxy(f"{self.path}['{key}']") return PlaceholderProxy(f"{self.path}[{key}]") def __str__(self) -> str: # 转为字符串时返回完整占位符 return f"{{{self.path}}}" def __format__(self, format_spec: str) -> str: # 处理带格式说明符的情况,比如 {name:!r} if format_spec: return f"{{{self.path}:{format_spec}}}" return f"{{{self.path}}}"
然后自定义Formatter,重写get_field方法——这个方法是string.Formatter中负责解析完整占位符(包括属性/索引链)的核心方法,我们在这里捕获所有可能的缺失异常,返回PlaceholderProxy:
import string class SafeFormatter(string.Formatter): def get_field(self, field_name: str, args, kwargs): try: # 尝试正常解析占位符 return super().get_field(field_name, args, kwargs) except (KeyError, AttributeError, IndexError): # 任意一层缺失时,返回代理对象和空格式说明符 return (PlaceholderProxy(field_name), "")
测试你的场景
先定义你示例中的Name类:
class Name: def __init__(self, name): self.name = name self.capitalized = name.capitalize() def __str__(self): return self.name
然后用SafeFormatter进行格式化测试:
formatter = SafeFormatter() # 正常解析的情况 print(formatter.format("hello, {name}!", name=Name("bob"))) # 输出: hello, bob! print(formatter.format("greetings, {name.capitalized}!", name=Name("bob"))) # 输出: greetings, Bob! # 顶层键不存在的情况 print(formatter.format("hello, {name}!", age=34)) # 输出: hello, {name}! print(formatter.format("greetings, {name.capitalized}!", age=34)) # 输出: greetings, {name.capitalized}! # 顶层键存在但属性不存在的情况 print(formatter.format("oops, {name.non_existent}!", name=Name("bob"))) # 输出: oops, {name.non_existent}! # 处理字典索引的情况 print(formatter.format("age is {user['age']}!", user={"name": "bob"})) # 输出: age is {user['age']}! # 带格式说明符的情况 print(formatter.format("hello, {name:!r}!", age=34)) # 输出: hello, {name:!r}!
这样所有场景都能满足你的需求:无论是顶层键缺失、属性缺失,还是索引缺失,都会返回原占位符,完全不会抛出错误,同时正常的占位符能正确解析。
备注:内容来源于stack exchange,提问作者Pearoo
相关产品推荐
相关产品推荐

