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

如何用装饰器对Python实例属性进行类型检查?

Python中用装饰器做实例属性类型检查的可行方案

你的需求完全可行,当前代码的问题出在静态类型检查器(如mypy)无法识别装饰器已经确保了maybe_none不为None,所以会提示返回值类型不兼容。运行时你的代码其实能正常触发检查,但需要调整代码让类型检查器理解装饰器的作用,或者补充类型断言。

方案一:给装饰器添加类型注解,让类型检查器识别其作用

通过typing模块的类型变量和类型转换,告诉类型检查器:被装饰的方法执行时,实例的maybe_none属性已被确保为非None状态。

import functools
from typing import Optional, Callable, TypeVar, cast

# 定义类型变量,用于泛型装饰器
Self = TypeVar('Self')
ReturnT = TypeVar('ReturnT')

def guarantee(func: Callable[[Self], ReturnT]) -> Callable[[Self], ReturnT]:
    @functools.wraps(func)
    def wrapper(self: Self, *args, **kwargs):
        if self.maybe_none is None:
            raise ValueError("maybe_none 不能为 None")
        # 给属性做类型断言,让类型检查器确认其非None
        self.maybe_none = cast(str, self.maybe_none)
        return func(self, *args, **kwargs)
    return wrapper

class MyClass:
    def __init__(self):
        self.maybe_none: Optional[str] = None

    def set(self, value: str):
        self.maybe_none = value

    @guarantee
    def get(self) -> str:
        return self.maybe_none  # 此时类型检查器不会再报错

方案二:在方法内部补充类型断言(更简洁)

如果不想修改装饰器的类型注解,可以在get方法内部添加assert语句,直接告诉类型检查器maybe_none不为None——虽然和装饰器的检查逻辑重复,但能快速解决类型提示问题:

import functools
from typing import Optional

def guarantee(func):
    @functools.wraps(func)
    def wrapper(self, *args, **kwargs):
        if self.maybe_none is None:
            raise ValueError
        return func(self, *args, **kwargs)
    return wrapper

class MyClass:
    def __init__(self):
        self.maybe_none: Optional[str] = None

    def set(self, value: str):
        self.maybe_none = value

    @guarantee
    def get(self) -> str:
        assert self.maybe_none is not None, "maybe_none 不能为 None"
        return self.maybe_none

补充说明

  • 运行时两种方案都能正常触发ValueError检查,确保属性非None时才执行方法。
  • 方案一更适合需要复用装饰器的场景,能统一管理类型检查逻辑;方案二更适合简单场景,代码改动更小。

内容的提问来源于stack exchange,提问作者Rick

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 07:45:30