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

如何自定义运算符重载使运算返回自定义NumberBase类实例

问题根源

当使用bn + 2这类运算符语法时,Python会直接调用类层面的__add__方法,而非实例上的方法。由于你的元类直接将int的原始方法绑定到了NumberBase类上,而你仅在__init__中对实例的方法进行了装饰,导致运算符调用的是未被装饰的int.__add__,返回结果自然是int类型。而直接调用bn.__add__(2)时,使用的是实例上被装饰后的方法,因此能正确返回NumberBase实例。

解决方案

修改元类逻辑,在创建NumberBase类时就直接装饰所有int的特殊运算符方法,确保类层面的方法也能返回NumberBase实例。同时移除__init__中对实例方法的装饰逻辑,避免冗余。

修改后的完整代码

import inspect

class _MetaNumberBase(type):
    def __new__(cls, name, bases, dct):
        # 收集int独有的特殊方法名
        cls_attrs = dir(cls)
        _int_attrs = []
        # 定义装饰器,用于包装int的方法,将返回值转为NumberBase
        def decorate(method):
            def wrapper(self, *args, **kwargs):
                result = method(self, *args, **kwargs)
                if isinstance(result, int):
                    return NumberBase(result, base_map=self.base_map, null=self.null)
                return result
            return wrapper

        for base in bases:
            for attr in dir(int):
                if attr not in cls_attrs and attr != "_int_attrs" and attr.startswith("__"):
                    base_attr = getattr(base, attr)
                    if not inspect.isdatadescriptor(base_attr) and callable(base_attr):
                        _int_attrs.append(attr)
                        # 直接装饰类方法并赋值
                        dct[attr] = decorate(base_attr)
        dct["_int_attrs"] = _int_attrs
        return super().__new__(cls, name, bases, dct)

class NumberBase(int, metaclass=_MetaNumberBase):
    _int_attrs: list[str]

    def __new__(cls, x: int, base_map, null=None):
        return super().__new__(cls, x)
    
    def __init__(self, x: int, base_map, null=None) -> None:
        self.base = len(base_map)
        self.base_map = tuple(base_map)
        self._field = dict(enumerate(base_map))

        self.null = null if null is not None else self._field[0]
        
        self._immutable = ("base", "base_map", "null", "_immutable", "_locked")
        self._locked = True

    def to_base(self, base_map, null=None):
        return NumberBase(self, base_map, null)

    def __setattr__(self, name, value):
        if hasattr(self, '_locked') and name in self._immutable:
            raise AttributeError(f"The '{name}' attribute is immutable")
        super().__setattr__(name, value)

    def __delattr__(self, name):
        if name in self._immutable:
            raise AttributeError(f"The '{name}' attribute is immutable")
        super().__delattr__(name)

    def __iter__(self):
        result = []
        num = self
        if num == 0:
            yield self.null
            return 
        elif self.base == 1:
            result = [self.base_map[0] for _ in range(self)]
        else:
            while num > 0:
                remainder = num % self.base
                result.append(self._field[remainder])
                num //= self.base
            result.reverse()
        yield from result

    def __str__(self) -> str:
        return ''.join(str(x) for x in self)

    def __repr__(self):
        return f"[bn]: {str(self)}"

if __name__ == "__main__":
    bn = NumberBase(34, [0, 1]) # 二进制

    print(repr(bn)) # [bn]: 100010
    print(int(bn))  # 34

    # 测试运算符+
    s = bn + 2
    print(repr(s)) # [bn]: 100100
    print(int(s))  # 36

    # 测试反向运算符(如数字在前)
    s = 2 + bn
    print(repr(s)) # [bn]: 100100
    print(int(s))  # 36

    # 测试其他运算符
    s = bn * 2
    print(repr(s)) # [bn]: 1000100
    print(int(s))  # 68

关键改动说明

  1. 元类中直接装饰方法:在元类的__new__方法内定义装饰器,对每个收集到的int特殊方法进行包装,确保类层面的方法返回NumberBase实例。
  2. 移除实例层面的装饰逻辑:删除__init__中循环装饰实例方法的代码,因为类层面已经完成了方法装饰,实例继承类的方法即可。
  3. 优化迭代器实现:将while result: yield result.pop(0)替换为yield from result,更简洁高效。
  4. 覆盖反向运算符:元类会自动处理__radd__、__rmul__等反向方法,确保2 + bn这类场景也能返回NumberBase实例。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 19:13:19