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

如何使MyPy识别校验类连接属性的Python装饰器类型断言

让MyPy认可装饰器集中校验连接非空的解决方案

问题背景

原代码通过在每个方法内手动校验_connection是否为None,能通过MyPy类型检查;但改用requires_connection装饰器集中处理校验后,MyPy无法识别装饰器已确保_connection非空,抛出以下错误:

error: Item "None" of "Connection | None" has no attribute "read"  [union-attr]
error: Item "None" of "Connection | None" has no attribute "write"  [union-attr]

解决方案

方案一:在装饰器内断言并重新赋值连接变量

修改装饰器代码,将cast后的连接变量重新赋值给self._connection,让MyPy识别此时变量已非空:

from functools import wraps
from typing import cast

class Connection:
    def read(self) -> str:
        return "data"
    
    def write(self, data: str) -> None:
        print(data)

def requires_connection(func):
    @wraps(func)
    def decorator(self, *args, **kwargs):
        if self._connection is None:
            raise RuntimeError("Connection not created")
        # 断言类型并重新赋值,告知MyPy此时_connection非空
        self._connection = cast(Connection, self._connection)
        return func(self, *args, **kwargs)
    return decorator

class Foo:
    def __init__(self):
        self._connection: None | Connection = None
        
    def connect(self):
        self._connection = Connection()
        
    @requires_connection
    def get_data(self) -> str:
        return self._connection.read()
        
    @requires_connection
    def send_data(self, data: str):
        self._connection.write(data)

方案二:使用类型守卫(TypeGuard)定义校验逻辑

在Foo类中定义一个类型守卫方法,装饰器调用该方法后,MyPy会自动推断_connection的非空状态:

from functools import wraps
from typing import TypeGuard
# Python 3.11+可直接用typing.Self,低版本需从typing_extensions导入
from typing_extensions import Self

class Connection:
    def read(self) -> str:
        return "data"
    
    def write(self, data: str) -> None:
        print(data)

class Foo:
    def __init__(self):
        self._connection: None | Connection = None
        
    def connect(self):
        self._connection = Connection()
    
    # 类型守卫方法:校验通过则断言实例的_connection非空
    def _has_valid_connection(self) -> TypeGuard[Self]:
        if self._connection is None:
            raise RuntimeError("Connection not created")
        return True

def requires_connection(func):
    @wraps(func)
    def decorator(self: Foo, *args, **kwargs):
        self._has_valid_connection()
        return func(self, *args, **kwargs)
    return decorator

class Foo:
    # ... 其余代码不变 ...
    @requires_connection
    def get_data(self) -> str:
        return self._connection.read()
        
    @requires_connection
    def send_data(self, data: str):
        self._connection.write(data)

说明

  • 方案一实现简单,仅需修改装饰器代码,适合快速解决问题;
  • 方案二更符合类型系统规范,类型守卫的逻辑可复用,适合复杂类结构或多场景校验需求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 04:32:03