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

解决Python装饰器中的TypeVar类型检查错误

解决装饰器类型注解的mypy错误

问题说明

我正在给装饰器加类型注解以通过mypy检查,参考相关规范和帖子后有进展,但碰到了以下错误:

decorators.py:12: error: A function returning TypeVar should receive at least one argument containing the same TypeVar [type-var]
decorators.py:12: note: Consider using the upper bound "Callable[..., Any]" instead

我已经在代码里用了bound=Callable[..., Any],还是搞不定,相关代码如下:

"""Decorator Functions."""

from typing import Any, Callable, List, TypeVar, Union, cast
from pandas import DataFrame, concat

F = TypeVar('F', bound=Callable[..., Any])


# Decorator to process DataFrame with ignore columns
def process_without_columns(
    ignore_cols: List[str], final_cols_order: Union[List[str], None] = None
) -> F:
    """
    Decorate to process a DataFrame, removing specified ignore columns, and then joining them back.

    Parameters
    ----------
    ignore_cols: List[str]
        List of column names to ignore during processing.
    final_cols_order: Union[List[str], None]
        List specifying the desired order of columns in the final DataFrame.
        If None, the original DataFrame's column order will be used. Default is None.

    Returns
    -------
        decorator_process: Decorator function that processes the DataFrame.
    """

    def decorator_process(func: F) -> F:
        def inner(self, data_df: DataFrame, *args: Any, **kwargs: Any) -> DataFrame:
            """
            Inner function that performs the actual processing of the DataFrame.

            Parameters
            ----------
                data_df: DataFrame
                    DataFrame to be processed.
                *args
                    args passed into inner function
                **kwargs
                    Kwargs passed into inner function


            Returns
            -------
                DataFrame: Processed DataFrame with the original columns
            """
            ignore_df = data_df[
                ignore_cols
            ]  # Extract the ignore columns as a separate DataFrame
            data_df = data_df.drop(
                columns=ignore_cols
            )  # Remove the ignore columns from the original DataFrame

            # Process the DataFrame (smaller DataFrame without ignore columns)
            processsed_df = func(self, data_df, *args, **kwargs)

            # Join back the processed DataFrame with the ignore columns DataFrame
            processsed_df = concat([processsed_df, ignore_df], axis=1)

            # Reorder DataFrame columns if final_cols_order is specified
            if final_cols_order is not None:
                processsed_df = processsed_df[final_cols_order]

            return processsed_df

        return cast(F, inner)

    return cast(F, decorator_process)

错误原因

核心问题是process_without_columns的返回类型标注错了:你把它标成了F,但这个函数是带参数的装饰器工厂,它返回的是一个装饰器(也就是接收F类型函数、再返回F类型函数的函数),而不是直接返回F类型的函数。

mypy的报错提示很明确:返回TypeVar的函数必须至少有一个参数包含该TypeVar,否则没法推断这个TypeVar的具体类型。你现在的process_without_columns没有接收F类型的参数,却标注返回F,这就违反了规则。

修复方案

  1. 修正装饰器工厂的返回类型:把-> F改成-> Callable[[F], F],明确它返回的是一个“接收被装饰函数、返回同类型函数”的装饰器。
  2. 优化TypeVar的边界定义:让F更精确地匹配被装饰函数的签名(因为你的被装饰函数是实例方法,带self参数,接收DataFrame并返回DataFrame)。
  3. 移除不必要的类型强制转换:原来的cast(F, decorator_process)是错误的,现在返回类型正确了,不需要这个强制转换。

修复后的代码

"""Decorator Functions."""

from typing import Any, Callable, List, TypeVar, Union, cast
from pandas import DataFrame, concat

# 限定F为带self、DataFrame参数,返回DataFrame的实例方法类型
F = TypeVar('F', bound=Callable[['Any', DataFrame, *Any, **Any], DataFrame])


def process_without_columns(
    ignore_cols: List[str], final_cols_order: Union[List[str], None] = None
) -> Callable[[F], F]:  # 正确标注返回类型:接收F,返回F的装饰器
    """
    装饰器:处理DataFrame时先移除指定列,处理完成后再合并回原列,支持指定最终列顺序。

    参数
    ----------
    ignore_cols: List[str]
        处理过程中需要暂时忽略的列名列表
    final_cols_order: Union[List[str], None]
        最终DataFrame的列顺序,为None则保留原数据列顺序,默认值None

    返回
    -------
        decorator_process: 用于处理DataFrame的装饰器函数
    """

    def decorator_process(func: F) -> F:
        def inner(self, data_df: DataFrame, *args: Any, **kwargs: Any) -> DataFrame:
            """
            执行实际DataFrame处理逻辑的内部函数

            参数
            ----------
                data_df: DataFrame
                    需要处理的DataFrame对象
                *args
                    传递给被装饰函数的位置参数
                **kwargs
                    传递给被装饰函数的关键字参数

            返回
            -------
                DataFrame: 恢复原忽略列后的处理结果DataFrame
            """
            ignore_df = data_df[ignore_cols]
            data_df = data_df.drop(columns=ignore_cols)

            # 调用原函数处理去除忽略列后的DataFrame
            processed_df = func(self, data_df, *args, **kwargs)

            # 合并回之前忽略的列
            processed_df = concat([processed_df, ignore_df], axis=1)

            # 如果指定了最终列顺序,重新排列
            if final_cols_order is not None:
                processed_df = processed_df[final_cols_order]

            return processed_df

        return cast(F, inner)  # 这里的cast是必要的,因为inner的签名和func一致,但mypy需要显式确认

    return decorator_process

额外说明

  • 优化后的F边界定义让类型检查更严格,确保被装饰的函数符合实例方法的签名要求。
  • 内部的cast(F, inner)是合理的:因为inner的功能是包装func,签名和返回值类型都和func一致,mypy无法自动推断这一点,所以需要显式转换。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 11:17:32