如何自定义运算符重载使运算返回自定义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
关键改动说明
- 元类中直接装饰方法:在元类的
__new__方法内定义装饰器,对每个收集到的int特殊方法进行包装,确保类层面的方法返回NumberBase实例。 - 移除实例层面的装饰逻辑:删除
__init__中循环装饰实例方法的代码,因为类层面已经完成了方法装饰,实例继承类的方法即可。 - 优化迭代器实现:将
while result: yield result.pop(0)替换为yield from result,更简洁高效。 - 覆盖反向运算符:元类会自动处理
__radd__、__rmul__等反向方法,确保2 + bn这类场景也能返回NumberBase实例。
内容的提问来源于stack exchange,提问作者ExpressMpeg
相关产品推荐
相关产品推荐

