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

自定义__add__方法的DailyAmounts类触发mypy运算符与索引错误

DailyAmounts类的Mypy错误分析与解决

问题背景

定义了如下DailyAmounts类:

from datetime import date
from typing import Dict, Callable, Iterator

class DailyAmounts:
    def __init__(self) -> None:
        self.daily_map: Dict[date, int] = dict()
        self.total: int = 0

    def __daily_math(self: 'DailyAmounts', other: 'DailyAmounts', function: Callable[[int, int], int]) -> 'DailyAmounts':
        if not isinstance(other, DailyAmounts):
            raise ValueError(f"Only {DailyAmounts.__name__} can be operated with")

        res = DailyAmounts()
        days = list(set(self.daily_map.keys()) | set(other.daily_map.keys()))
        for day in days:
            amount = function(self.daily_map.get(day, 0), other.daily_map.get(day, 0))
            res[day] = amount

        return res
    
    def __add__(self: 'DailyAmounts', other: 'DailyAmounts') -> 'DailyAmounts':
        def add(a: int, b: int) -> int:
            return a + b
        return self.__daily_math(other, add)

    def __sub__(self: 'DailyAmounts', other: 'DailyAmounts') -> 'DailyAmounts':
        def sub(a: int, b: int) -> int:
            return a - b
        return self.__daily_math(other, sub)

    def __iter__(self: 'DailyAmounts') -> Iterator[date]: 
        return iter(self.daily_map)

以及包含DailyAmounts属性的Item类:

class Item:
    def __init__(self) -> None:
        self.daily_amounts: DailyAmounts = DailyAmounts()  # 值后续在其他地方计算

使用时出现两个Mypy错误:

  1. 运算符错误:
def some_function(old_item: Item):
    new_item = Item()
    # 错误提示:Unsupported operand types for + ("DailyAmounts" and "Callable[..., Any]")  [operator]
    new_item.daily_amounts = old_item.daily_amounts + DailyAmounts()  # 复制实例
  1. 索引赋值错误:
def another_function(my_item: Item, my_other_item: Item):
    # 错误提示:Unsupported target for indexed assignment ("Callable[..., Any]")  [index]
    for entry in my_item.daily_amounts.daily_map.items():
        my_other_item.daily_amounts[entry[0]] = entry[1]

错误原因

1. 运算符错误原因

  • 类型注解解析问题:类内部使用字符串形式的'DailyAmounts'作为类型注解,但未启用延迟注解求值,导致Mypy无法正确识别__add__方法的参数类型,误将DailyAmounts()实例推断为可调用对象。
  • 缺失类型导入:如果未导入date、Dict等核心类型,Mypy会将这些类型视为Any,干扰类型推断逻辑。

2. 索引赋值错误原因

DailyAmounts类未实现映射协议的核心方法__setitem__,Mypy判定该类实例不支持[]赋值操作。虽然可以通过daily_map直接访问内部字典,但直接对DailyAmounts实例使用索引赋值时,Mypy无法识别操作的合法性。

解决方法

1. 修复运算符错误

  • 启用延迟注解求值:在文件顶部添加from __future__ import annotations(Python 3.7+支持),让Mypy正确解析类内部的字符串类型注解。
  • 补全类型导入:确保已导入from datetime import date和from typing import Dict, Callable, Iterator。

2. 修复索引赋值错误

给DailyAmounts类添加__setitem__方法,同时建议补充__getitem__以完善映射协议支持:

class DailyAmounts:
    # 其他方法保持不变
    def __setitem__(self, day: date, amount: int) -> None:
        self.daily_map[day] = amount
        # 可选:同步更新total值,保证数据一致性
        old_amount = self.daily_map.get(day, 0)
        self.total += amount - old_amount

    def __getitem__(self, day: date) -> int:
        return self.daily_map[day]

额外优化

如果需要直接在DailyAmounts实例上调用items()(无需通过daily_map),可以添加如下方法:

def items(self) -> Iterator[tuple[date, int]]:
    return self.daily_map.items()

修改后,another_function可简化为:

def another_function(my_item: Item, my_other_item: Item):
    for day, amount in my_item.daily_amounts.items():
        my_other_item.daily_amounts[day] = amount

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 15:47:47