You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.14 17:38:00