Python自定义异常为何显示模块名?异常处理写法优化咨询
Python类型检查装饰器相关问题解答
1. 自定义异常显示模块名的原因
Python中自定义异常类如果定义在某个模块(比如你的strict.py)里,默认打印异常时会带上模块名.类名,这是Python的默认规则——而内置异常属于builtins模块,Python对其做了特殊处理,不会显示模块名。
如果想让自定义异常打印时只显示类名,可以重写__repr__和__str__方法:
class TypeNotFoundError(TypeError): def __repr__(self): return self.__class__.__name__ def __str__(self): return self.__class__.__name__
要是需要附带错误信息,就把内容整合进去:
class TypeNotFoundError(TypeError): def __init__(self, msg="未找到类型注解"): super().__init__(msg) def __repr__(self): return f"{self.__class__.__name__}: {self.args[0]}" def __str__(self): return f"{self.__class__.__name__}: {self.args[0]}"
2. 回溯显示报错行的差异
内置异常抛出时,Python会自动隐藏部分内部调用栈(比如装饰器、内置函数的调用行);而自定义异常的回溯会完整展示所有调用链路,包括装饰器里抛出异常的那一行。
如果想让回溯更简洁,可以用raise ... from None切断异常链,减少冗余信息:
def type_check_decorator(func): def wrapper(*args, **kwargs): if not func.__annotations__: raise TypeNotFoundError("函数未定义类型注解") from None # 其他类型检查逻辑 return func(*args, **kwargs) return wrapper
from None会让Python只显示当前异常的回溯,效果更接近内置异常。
3. 直接使用raise是否可行
当然可行,这才是更合理的写法。如果你的try-except只是用来捕获自身逻辑的错误再重抛,完全没必要多余包裹,直接在条件不满足时用raise抛出即可:
# 不推荐的生硬写法 def wrapper(*args, **kwargs): try: if not func.__annotations__: raise TypeNotFoundError() except Exception as e: raise e # 推荐的直接写法 def wrapper(*args, **kwargs): if not func.__annotations__: raise TypeNotFoundError("函数未提供类型提示") # 后续检查逻辑
直接raise代码更清晰,避免无意义的异常捕获和重抛。
4. 类型检查装饰器的标准实现模式
自己实现的话,利用inspect模块处理参数和注解是更可靠的方案,能覆盖位置参数、关键字参数、默认参数等场景。示例实现如下:
import inspect class TypeNotFoundError(TypeError): def __init__(self, func_name): super().__init__(f"函数'{func_name}'未定义类型注解") def __repr__(self): return f"{self.__class__.__name__}: {self.args[0]}" def __str__(self): return self.args[0] def type_check(func): sig = inspect.signature(func) annotations = func.__annotations__ if not annotations: raise TypeNotFoundError(func.__name__) def wrapper(*args, **kwargs): # 绑定参数,自动处理默认值 bound_args = sig.bind(*args, **kwargs) bound_args.apply_defaults() # 逐个检查参数类型 for param_name, value in bound_args.arguments.items(): if param_name in annotations: expected_type = annotations[param_name] if not isinstance(value, expected_type): raise TypeError( f"参数'{param_name}'预期类型为{expected_type.__name__},实际传入{type(value).__name__}" ) return func(*args, **kwargs) return wrapper # 测试用例 @type_check def test(a: int, b: str): print(a, b) # 未加注解的函数,应用装饰器时会直接抛出异常 # @type_check # def test_no_hint(a, b): # print(a, b)
如果不想重复造轮子,typeguard这类成熟库已经实现了完善的运行时类型检查,直接用就行,但自己实现确实是很好的学习过程。
内容的提问来源于stack exchange,提问作者gberenzy
相关产品推荐
相关产品推荐

