应于属性值首次承担业务意义时抛出ValueError,即在__init__或@property setter中对非法值(如负数年龄、空用户名)显式校验并raise ValueError,而非在I/O操作或可选参数为None时误用;复杂语义宜自定义异常类。

类属性赋值时类型或范围不合法就该raise ValueError
当你在 __init__ 或 @property setter 中检测到传入值明显违背业务约束(比如负数年龄、空字符串用户名、超出范围的端口号),直接 raise ValueError 是最自然的选择。它比返回错误码或静默修正更明确,也符合 Python 的“显式优于隐式”原则。
- 只在**确定无效**时抛出,不要用它替代条件分支逻辑
- 避免在
__init__里验证可选参数是否为None—— 那属于正常控制流,不是错误 - 如果属性允许延迟初始化(比如设为
None后再赋值),验证应放在 setter 而非__init__
用@property setter做实时校验比__init__更灵活
很多开发者只在 __init__ 里做一次校验,但类实例化后属性仍可能被非法修改。用 @property + @xxx.setter 包裹属性,就能在每次赋值时重新触发 raise ValueError。
- setter 中的
raise ValueError能拦截所有非法写入,包括外部直接赋值:obj.age = -5 - 注意:不要在 setter 里调用
self.xxx = value(会无限递归),要用私有属性存储,比如self._age = value - 如果属性是只读的,干脆不定义 setter,连赋值机会都不给
别在__init__里raise ValueError处理I/O或网络依赖
__init__ 的职责是初始化对象状态,不是执行外部操作。如果你在构造函数里尝试打开文件、连接数据库、解析远程 JSON 并因此 raise ValueError,就混淆了关注点。
- 这类失败更应抛出
FileNotFoundError、ConnectionError等具体异常,而不是笼统的ValueError - 把 I/O 操作移出
__init__,放到独立方法中(如load_config()),让调用者决定何时触发、如何重试 -
ValueError只针对“值本身有问题”,不针对“值对应的资源不可达”
自定义异常类比ValueError更适合复杂业务语义
当 ValueError 的语义开始模糊(比如“邮箱格式错误”和“密码强度不足”都抛同一个异常),捕获方很难区分处理逻辑。这时应该定义自己的异常类,再在需要时 raise 它。
立即学习“Python免费学习笔记(深入)”;
- 继承
Exception或更具体的内置异常(如ValueError),便于上游统一捕获或单独处理 - 异常类名要能表达业务含义,比如
InvalidEmailError、InsufficientBalanceError - 在
__init__中保留原始错误信息,必要时用raise ... from e构建异常链
raise ValueError 放在那个「值第一次承担业务意义」的位置。


















