27 KiB
06 - 面向对象与运算符重载
Viper 中的 class 有两种语义:数据布局(结构体/联合体/枚举,见 05-classes.md)和面向对象。OOP 能力由类内是否存在方法自动判定(StructEntry.IsOOP);多态用 virtual,非多态继承(C++ 风格,无 vtable 开销)用 novirtual。
@t.Object装饰器默认不需要写、也尽量避免使用——OOP 由「类内是否有方法」自动判定,写与不写行为一致。这是本文唯一一次提及,新代码请直接省略。
类修饰符(virtual / novirtual)
virtual / novirtual 是前置软关键字,类级与方法级都可用:
virtual class Shape: # 类级:默认该类所有方法都进虚表
def area(self) -> float: ...
novirtual def helper(self) -> int: ... # 只有 helper 被排除出虚表
novirtual class Node: # 类级:默认该类所有方法都非虚
def plain(self) -> int: ...
virtual def hooked(self) -> int: ... # 只有 hooked 进虚表
virtual class/novirtual class:类级修饰符——默认该类所有方法都采用这个级别;virtual def/novirtual def:方法级修饰符——只改变单个方法、覆盖类级默认,只能直接写在类体内;- 例:
novirtual类里某个方法想进虚表,写novirtual class C:+virtual def m(self);反之virtual类里把某方法排除出虚表,写virtual class C:+novirtual def m(self); - 非法形态(修饰符位置错误、
virtual virtual等)编译期 fail-fast。
面向对象(OOP)
类内定义成员方法即拥有面向对象支持(成员方法、运算符重载、属性装饰器等);编译器根据「结构体内是否有函数定义」自动判定 OOP 行为。
基本定义
class ViperKernel:
BootInfo: bootinfo*
VGA: vga._VGAScreenDriver*
VGA_drv: vga._VGAScreenDriver
def __init__(self):
self.BootInfo = BootInfo
serial.init()
serial.puts("ViperOS VKernel Test\n")
避免在 __init__ 中使用 return,虽然不会报错,但是你永远无法获得 return 的结果,如果需要提前结束代码流,return 也不失为一种办法。
成员方法
class Sheet:
id: int
x: int
y: int
w: int
h: int
def __init__(self, x: int, y: int, w: int, h: int):
self.x = x
self.y = y
self.w = w
self.h = h
def MoveTo(self, nx: int, ny: int):
self.x = nx
self.y = ny
def Resize(self, nw: int, nh: int):
self.w = nw
self.h = nh
方法调用
sheet: Sheet = Sheet(0, 0, 640, 480)
sheet.MoveTo(100, 100)
sheet.Resize(800, 600)
属性装饰器
支持 Python 的 @property 和 @xxx.setter 装饰器:
@xxx.setter和自定义装饰器正在试验阶段,尽可能不要使用
class MyObj:
_value: int
@property
def value(self) -> int:
return self._value
@value.setter
def value(self, v: int):
self._value = v
静态方法
@staticmethod
def _yield():
pass
对象指针与解引用
对象类实例通常通过指针操作:
obj_ptr: MyClass* = &MyClass()
obj: MyClass = *obj_ptr
成员访问
通过 self 访问成员变量和方法:
def method(self) -> void:
self.x = 10
y: int = self.y
self.DoSomething()
内嵌对象
对象类可以包含其他对象实例(非指针):
class Sheet:
_surf_obj: gfx.Surface
_renderer_obj: gfx.Renderer
surf: gfx.Surface*
renderer: gfx.Renderer*
def __init__(self, fb: uint32*, zb: float*, w: int, h: int):
self._surf_obj = gfx.Surface(fb, zb, w, h)
self.surf = &self._surf_obj
self._renderer_obj = gfx.Renderer(self.surf)
self.renderer = &self._renderer_obj
__before_init__ 方法
仅当类内含方法或继承虚方法时才生成 __before_init__ 方法(纯数据 class 不生成)。它在 __init__ 之前自动填充成员默认值,并对 virtual 类初始化 vtable:
define void @"<SHA1>.ClassName.__before_init__"(%"<SHA1>.ClassName"* %self) {
entry:
; 对 virtual 类:存储 vtable 指针到 slot 0
; 遍历所有成员:若有默认值则 store 默认值,否则 store 零值
ret void
}
职责:
- 填充类定义中声明的成员默认值(如
x: int = 0中的0) - 对无默认值的成员填充类型零值
- 对
virtual类额外初始化 vtable 指针
调用时机(由编译器在构造时自动插入):
- 不存在
__new__时:在alloca分配之后调用一次,随后进入__init__ - 存在
__new__时:在__new__之后对最终存储指针调用一次,随后进入__init__
关于 __new__:
__new__返回self时使用默认的alloca存储__new__的签名可与__init__不同__new__与virtual兼容__new__本身不入虚表
就地构造:用户也可手动调用 __before_init__ 实现就地构造(placement new 语义),典型用法见 mpool.alloc_buf:
buf: Buf* = pool.alloc(Buf.__sizeof__())
buf.__before_init__() # 填充默认值
buf.__init__(args) # 用户初始化逻辑
跨模块导入类时,导入侧也会声明 __before_init__,保证跨模块构造行为一致。
novirtual 非多态继承
novirtual 启用 C++ 风格的非多态继承(对应 struct A : B {} 无 virtual)。与 virtual 的区别:
| 特性 | novirtual |
virtual |
|---|---|---|
| vtable 指针 | ❌ 无 | ✅ 自动插入 slot 0 |
| 虚派发 | ❌ 无 | ✅ 支持 |
| 方法继承 | ✅ 调用点解析直调父类 | ✅ 通过 vtable |
| 字段展平 | ✅ 父类字段前置嵌入 | ✅ 父类字段前置嵌入(vtable 之后) |
| 运行时开销 | 零 | 每次虚调用一次间接跳转 |
基本定义
novirtual class LinkedNode:
next: LinkedNode*
prev: LinkedNode*
def append(self, node: LinkedNode*): ...
novirtual class GNode(LinkedNode): # 非多态继承
value: int
子类 GNode 的结构体布局为 {next, prev, value}(父类字段前置,无 vtable 指针)。
方法继承机制
子类调用父类方法时,实际机制为调用点解析:编译器沿 MRO(方法解析顺序)定位到定义该方法的父类,按字节偏移调整 this 指针后 bitcast,在调用点直接调用父类实现(无 wrapper 生成、无尾调用)。
这里的 MRO 为「深度优先 + 声明顺序 + 去重」(含自身),不是 Python 的 C3 线性化;详见 14-multiple-inheritance.md。
跨模块继承
跨模块父类(如 linkedlist.LinkedNode)的 novirtual 标记会在导入时被检测并记录,子类继承时同样跳过 vtable 插入。
向下转型
父类指针→子类指针需显式转型(生成 bitcast IR):
root: Node = Node() # Node 继承 LinkedNode
child: LinkedNode* = root.child
# child 是 LinkedNode*,需要转回 Node*:
node: Node* = (Node*)(child)
隐式转型仅向上(子→父)。
virtual 虚函数表与多态
规划:将引入
T1 & T2(TypeIntersection)表达 object(尤其是带 vtable 的 object)的交集类型,作为多态对象组合的静态类型手段(详见 02-type-system.md 类型组合规划演进,尚未实现)。
尽可能避免使用
self[0]来获取结构体成员,其不稳定,也不要手动操作虚表。
用 virtual(类级)启用虚函数表支持。启用后,结构体第一个成员自动插入 i8* 类型的 vtable 指针。类内需有方法(OOP 行为由「结构体内是否有函数」自动判定)。
基本定义
virtual class Base:
def __init__(self):
pass
def method(self) -> int:
return 0
在 LLVM IR 中,virtual 类的结构体类型自动插入 vtable 指针:
%"<SHA1>.Base" = type { i8*, i32 }
同时,编译器会生成全局 vtable 变量。vtable 实为 internal constant struct{ n x i8* }——即含 n 个 i8* 函数指针成员的结构体常量(成员为带 bitcast 的初始化文本),变量名为 <sha1>.Class.vtable,并非 [1 x i8*] zeroinitializer:
@"<sha1>.Base.vtable" = internal constant { i8* } {
i8* bitcast (i32 (%"<sha1>.Base"*)* @"<sha1>.Base.method" to i8*)
}
继承与多态
虚表可以帮你使用继承语法实现多态:
virtual class Base:
def __init__(self):
pass
def method(self) -> int:
return 0
virtual class M1(Base):
def method(self) -> int:
return 1
当 M1 被初始化时,VTable 会自动修改,这点和 C++ 相同,尽可能避免直接操作 VTable。
继承时,子类会自动继承父类的成员变量和方法。子类重写父类方法时,vtable 中对应位置的函数指针被子类方法替换,实现多态。
VTable 运行时修改
如果需要临时构造一个额外的 Base 结构体,还可以使用:
virtual class Base:
def __init__(self):
pass
def method(self) -> int:
return 0
def __method1(self) -> int:
return 1
export def main() -> int:
b: Base = Base()
b.method = __method1
启用 VTable 的表中,允许直接修改内部函数为函数指针,在底层,此操作将修改 VTable 来达成目的。编译器会检测当前 vtable 是否与类 vtable 相同,如果相同则创建 vtable 的副本(memcpy),避免修改影响所有实例。
虚方法调度机制
虚方法调用的核心流程:
- 从对象指针 GEP 获取第 0 个 slot(vtable 指针)
- 加载 vtable 指针
- bitcast 为
[N x i8*]*类型 - GEP 获取方法索引处的函数指针
- 加载函数指针
- bitcast 为正确的函数指针类型
- 间接调用
方法级虚表开关
类级 virtual / novirtual 决定该类所有方法的默认级别;要单独改变某一个方法,就在该方法上加相反的方法级修饰符:
novirtual类中单独标注virtual def的方法进入虚表;virtual类中单独标注novirtual def的方法被排除在虚表之外。
novirtual class Node: # 默认:所有方法都非虚
def plain(self) -> int: ... # 跟随类级 → 非虚
virtual def hooked(self) -> int: ... # 仅 hooked 进虚表
virtual class Shape: # 默认:所有方法都进虚表
def area(self) -> float: ... # 跟随类级 → 虚
novirtual def helper(self) -> int: ... # 仅 helper 排除出虚表
以上行为由 func_vtable_test 实证。
super()
super().method(...) 形态被编译为静态父类方法调用(仅该形态被支持),不经过 vtable 动态派发:
virtual class Base:
def method(self) -> void:
pass
virtual class Derived(Base):
def method(self) -> void:
super().method() # 静态调用父类实现
继承
结构体成员继承
当子类继承父类(父类在 Gen.class_members 中存在)时:
- 父类的成员变量被继承到子类(排在子类成员之前)
- 父类的默认值也被继承
- 父类的方法被继承(重命名为
子类名.方法名)
class Animal:
name: [32]char
age: int
def __init__(self, age: int):
self.age = age
def Speak(self) -> void:
serial.puts("...\n")
class Dog(Animal):
breed: int
def Speak(self) -> void:
serial.puts("Woof!\n")
异常类继承
Viper 支持异常类的继承,用于 try/except 的异常匹配:
class MyError(Exception):
pass
class SpecificError(MyError):
pass
详见 09-exceptions.md。
运算符重载
对象类支持运算符重载。编译器在遇到运算符时,先尝试左操作数的 dunder 方法;左操作数无对应方法时,回退右操作数的反向 dunder(a + b → 先 a.__add__(b),缺失再 b.__radd__(a))。TPC 与 VPC 两端行为一致。
算术运算符重载
| 运算符 | Dunder 方法 | 示例 |
|---|---|---|
+ |
__add__ |
a + b → a.__add__(b) |
- |
__sub__ |
a - b → a.__sub__(b) |
* |
__mul__ |
a * b → a.__mul__(b) |
/ |
__truediv__ |
a / b → a.__truediv__(b) |
// |
__floordiv__ |
a // b → a.__floordiv__(b) |
% |
__mod__ |
a % b → a.__mod__(b) |
** |
__pow__ |
a ** b → a.__pow__(b) |
& |
__and__ |
a & b → a.__and__(b) |
| |
__or__ |
a | b → a.__or__(b) |
^ |
__xor__ |
a ^ b → a.__xor__(b) |
<< |
__lshift__ |
a << b → a.__lshift__(b) |
>> |
__rshift__ |
a >> b → a.__rshift__(b) |
基础类型(整数/浮点)的
**由编译器直接 lower 为llvm.pow:整数结果在指数为非负整数常量时回写为左操作数类型,其余为浮点结果。自定义类型的**走__pow__重载。
示例:
class Vec2:
x: float
y: float
def __init__(self, x: float, y: float):
self.x = x
self.y = y
def __add__(self, other: Vec2) -> Vec2:
result: Vec2 = Vec2(0.0, 0.0)
result.x = self.x + other.x
result.y = self.y + other.y
return result
def __mul__(self, scalar: float) -> Vec2:
result: Vec2 = Vec2(0.0, 0.0)
result.x = self.x * scalar
result.y = self.y * scalar
return result
增量赋值运算符重载
增量赋值先调度对象的增量 dunder(+= → __iadd__),该类无增量 dunder 时回退对应普通 dunder(__add__);基础类型走原生运算。两端支持:
| 运算符 | 增量 Dunder | 回退 Dunder |
|---|---|---|
+= |
__iadd__ |
__add__ |
-= |
__isub__ |
__sub__ |
*= |
__imul__ |
__mul__ |
/= |
__itruediv__ |
__truediv__ |
//= |
__ifloordiv__ |
__floordiv__ |
%= |
__imod__ |
__mod__ |
**= |
__ipow__ |
__pow__ |
&= |
__iand__ |
__and__ |
|= |
__ior__ |
__or__ |
^= |
__ixor__ |
__xor__ |
<<= |
__ilshift__ |
__lshift__ |
>>= |
__irshift__ |
__rshift__ |
调度范围与 TPC 一致:仅局部变量目标调度增量 dunder(全局 /
nonlocal目标不调度)。重载返回值须与目标变量类型兼容(VPC 侧不兼容时编译期 fail-fast)。
比较运算符重载
__eq__/__ne__/__lt__/__le__/__gt__/__ge__ 已自动调度(映射与调用点自动调度,opovl_test 覆盖);未定义 __eq__ 时比较回退为默认身份比较。
| 运算符 | Dunder 方法 | 状态 |
|---|---|---|
== |
__eq__ |
已自动调度 |
!= |
__ne__ |
已自动调度 |
< |
__lt__ |
已自动调度 |
<= |
__le__ |
已自动调度 |
> |
__gt__ |
已自动调度 |
>= |
__ge__ |
已自动调度 |
反向运算符重载
左操作数无对应普通 dunder 时,自动调度右操作数的反向 dunder(a + b → b.__radd__(a))。两端支持:
| Dunder 方法 | 语义 |
|---|---|
__radd__ |
右操作数加法 |
__rsub__ |
右操作数减法 |
__rmul__ |
右操作数乘法 |
__rtruediv__ |
右操作数真除法 |
__rfloordiv__ |
右操作数整除 |
__rmod__ |
右操作数取模 |
__rpow__ |
右操作数幂运算 |
__rand__ |
右操作数按位与 |
__ror__ |
右操作数按位或 |
__rxor__ |
右操作数按位异或 |
__rlshift__ |
右操作数左移 |
__rrshift__ |
右操作数右移 |
一元运算符重载
__neg__/__pos__/__invert__ 已自动调度。
| 运算符 | Dunder 方法 | 状态 |
|---|---|---|
-a |
__neg__ |
已自动调度 |
+a |
__pos__ |
已自动调度 |
~a |
__invert__ |
已自动调度 |
可调用对象 __call__
对象类定义了 __call__ 时,obj(args) 自动分发到 obj.__call__(args)(两端已调度)。
class Counter:
value: int
def __init__(self):
self.value = 0
def __call__(self) -> int:
self.value += 1
return self.value
c: Counter = Counter()
x: int = c() # x = 1
y: int = c() # y = 2
下标访问重载
__getitem__ — 读取
def __getitem__(self, key: int) -> int:
return self.data[key]
__setitem__ — 赋值
def __setitem__(self, key: int, value: int):
self.data[key] = value
__delitem__ — 删除
def __delitem__(self, key: int):
pass
布尔转换 __bool__
对象类定义了 __bool__ 时,条件位置自动调度(两端已调度):if/elif/while 条件、and/or/not 操作数、三元表达式条件。
class Buffer:
size: int
data: [1024]char
def __bool__(self) -> bool:
return self.size > 0
buf: Buffer = Buffer()
if buf:
serial.puts("buffer is not empty\n")
长度 __len__
class Buffer:
size: int
def __len__(self) -> int:
return self.size
buf: Buffer = Buffer()
n: int = len(buf)
字符串转换 __str__
对象类定义了 __str__ 时,str(obj)、print(obj) 与 f-string(f"{obj}",未指定格式说明符)自动调度该方法,取其返回的字符串(const char*)输出(两端已调度)。
class Point:
x: int
y: int
def __str__(self) -> const char*:
return "Point"
p: Point = Point(1, 2)
print(p) # 调用 p.__str__() → 输出 "Point"
绝对值 __abs__
对象类定义了 __abs__ 时,abs(obj) 自动调度该方法(两端已调度);基础类型仍走内建/C 库 abs(labs),见 11-builtins.md。
class SignedValue:
val: int
def __abs__(self) -> int:
if self.val < 0:
return -self.val
return self.val
sv: SignedValue = SignedValue()
sv.val = -42
a: int = abs(sv) # a = 42
迭代器协议
Viper 的迭代器协议与 Python 相同。__next__ 带编译器注入的隐藏 i1* 参数 __stop_iter_flag__:方法内 raise StopIteration 会将该参数置位并返回默认值以结束迭代。
__iter__ 和 __next__
class IntRange:
start: int
end: int
current: int
def __init__(self, start: int, end: int):
self.start = start
self.end = end
self.current = start
def __iter__(self):
return self
def __next__(self, __stop_iter_flag__) -> int: # 编译器注入的隐藏 i1* 参数
if self.current >= self.end:
raise StopIteration
val: int = self.current
self.current += 1
return val
for 循环使用
r: IntRange = IntRange(0, 10)
for i in r:
print(i)
编译器生成的等价逻辑:
IntRange r = IntRange_init(0, 10);
IntRange iter = IntRange___iter__(&r);
while (1) {
i1 __stop_iter_flag__ = 0; /* 编译器注入的隐藏 i1* 参数 */
int i = IntRange___next__(&iter, &__stop_iter_flag__);
if (__stop_iter_flag__ == 1) break;
print_int(i);
}
上下文管理器
with 语句要求对象实现 __enter__ 和 __exit__ 方法:
class File:
fd: int
path: const char*
def __init__(self, path: const char*, flags: int):
self.path = path
self.fd = -1
def __enter__(self):
self.fd = fat32.open(self.path, self.flags)
return self
def __exit__(self):
fat32.close(self.fd)
def read(self) -> int:
return fat32.read(self.fd)
使用方式:
with File("/test.txt", FA_READ) as f:
data: int = f.read()
编译器会自动保证 __exit__ 在 with 块结束时被调用。
析构 __del__ / __delete__
del 的目标形态决定调度的 dunder(两端一致):
| 语句 | 调度 |
|---|---|
del obj |
obj.__delete__()(类未定义则静默无操作) |
del obj.attr |
property deleter(@attr.deleter);无 deleter 时回退属性值对象的 __del__() |
del obj[key] |
obj.__delitem__(key) |
del obj # 调用 obj.__delete__()
del arr[idx] # 调用 arr.__delitem__(idx)
isinstance
isinstance(obj, Cls) 不是关键字,而是编译器在调用点按函数名识别的内建(返回 i1)。
if isinstance(obj, Base):
...
if isinstance(obj, (A, B)): # 元组:任一命中即为真
...
语义:
virtual类(带 vtable):运行时读对象头部 vtable 指针,与「目标类及其全部已知子类的 vtable 地址集合」逐一比较,相等即真 ⇒ 正确覆盖多态子类(这也是运行期唯一能拿到实际类的途径);novirtual类 / 未知类(无 vtable):回退编译期静态 MRO 判定(据 obj 的静态类型)——可能保守判假。
第二参数支持:裸类名 / 跨模块 mod.Cls(只取短名)/ 元组 (A, B)。不支持 REnum 变体、t.CEnum 等。
限制:已知子类集合上限 64、元组上限 15;Viper 侧类型推断失败即 fail-fast(TypeError: isinstance() 参数类型推断失败)。
注意:胖节点 AST 的节点分派用的是虚方法
kind()(返回ASTKind),不是isinstance;文档中提到的vtype字段目前不存在(属迁移规划)。
FakeDuck(伪鸭子类型)
FakeDuck 让内建类型 str(LLVM 层就是 i8*)也能像对象一样调用方法、读写字段——通过扩展类实现,不是关键字 / 装饰器 / 基类。
启用方式(命名约定 + import)
- 扩展类必须命名为
_str(字符串扩展方法)或_Type(通用基类),定义在被import的模块中; - 使用者
import _fakeduck(提供_str/_Type);with自动注入还需import _withcontent。
内置扩展类见 includes/_fakeduck.vp:_Type.type_id();_str 提供 upper / lower / capitalize / swapcase / title / casefold / isupper / islower / isalpha / isdigit / isalnum / isspace / isascii / istitle / split 等。
扩展方法调用
import _fakeduck
a: str = "Hello"
tid: int = a.type_id() # 解析到扩展类 _Type.type_id
u: str = a.upper() # 解析到扩展类 _str.upper
分派是回退:先走普通结构体方法,全部落空后才尝试 FakeDuck;接收者注解是 list[...] 时跳过。方法名在 _Type / _str 中查找。
影子字段与 with 自动注入
编译期为每个裸 str 变量额外开一个影子结构体 <变量名>__meta__,用来存 per-instance 字段(__data__、__mbuddy__):
a.__mbuddy__ = bd # 语法糖,等价 a.set_mbuddy(bd)
d: str = a.__data__ # 读影子字段
with 上下文可自动注入字段:提供方类用类体注解 __provides__ = ['字段名'] 声明,消费方扩展类用 __requires__ = ['字段名'] 声明:
with memhub.MemBuddy(arena, 65536) as bd:
s: str = "WithTest" # __mbuddy__ 自动注入,无需手动赋值
u2: str = s.upper()
另有 __require_must__:表示该字段必须由某个外层 with 提供,否则编译期 fail-fast。
限制
- 影子结构体只对裸
str注解创建(bytes/char*不创建);_bytes扩展类未实现。 len()/ 下标[]/for迭代等不走 FakeDuck,而是list[T]的 dunder / 内建分派。with注入依赖运行时 provider 栈(includes/_withcontent.vp),未 import_withcontent时跳过注入。
真实用例:Test/FakeDuckTest。
Dunder 方法完整参考
已实现自动调度的 Dunder 方法
| Dunder 方法 | 触发语法 | 说明 |
|---|---|---|
__init__ |
ClassName(args) |
构造函数 |
__before_init__ |
自动生成 | 填充成员默认值并初始化 vtable(类内含方法或继承虚方法时才生成),在 __init__ 之前执行 |
__add__ |
a + b |
加法 |
__sub__ |
a - b |
减法 |
__mul__ |
a * b |
乘法 |
__truediv__ |
a / b |
真除法 |
__floordiv__ |
a // b |
整除 |
__mod__ |
a % b |
取模 |
__and__ |
a & b |
按位与 |
__or__ |
a | b |
按位或 |
__xor__ |
a ^ b |
按位异或 |
__lshift__ |
a << b |
左移 |
__rshift__ |
a >> b |
右移 |
__eq__ |
a == b |
等于(未定义 __eq__ 时默认身份比较) |
__ne__ |
a != b |
不等于 |
__lt__ |
a < b |
小于 |
__le__ |
a <= b |
小于等于 |
__gt__ |
a > b |
大于 |
__ge__ |
a >= b |
大于等于 |
__neg__ |
-a |
一元负号 |
__pos__ |
+a |
一元正号 |
__invert__ |
~a |
按位取反 |
__getitem__ |
obj[key] |
下标读取 |
__setitem__ |
obj[key] = value |
下标赋值 |
__delitem__ |
del obj[key] |
下标删除 |
__len__ |
len(obj) |
长度 |
__iter__ |
for x in obj: |
获取迭代器 |
__next__ |
for x in obj: |
取下一个值(隐藏 i1* 参数 __stop_iter_flag__,raise StopIteration 置位后返回默认值结束迭代) |
__enter__ |
with obj as x: |
上下文管理器进入 |
__exit__ |
with obj as x: |
上下文管理器退出 |
声明支持但未实现自动调度的 Dunder 方法
| Dunder 方法 | 预期触发语法 | 当前状态 |
|---|---|---|
__pow__ |
a ** b |
期望实现:binop ** 未调度(基础类型 ** 编译失败;自定义类型走 __pow__ 重载亦属期望) |
__iadd__ |
a += b |
期望实现:AugAssign 一律按原生运算符展开(+=→add、//=→sdiv …),不调用 __iadd__,也无 __add__ 回退 |
__isub__ |
a -= b |
同上 |
__imul__ |
a *= b |
同上 |
__itruediv__ |
a /= b |
同上 |
__ifloordiv__ |
a //= b |
同上 |
__imod__ |
a %= b |
同上 |
__ipow__ |
a **= b |
同上 |
__str__ |
str(obj) / print(obj) |
期望实现:print 为类型感知格式化(str/%s、int、float、struct 输出占位符),不调度 __str__ |
__call__ |
obj(args) |
期望实现:当前未调度 |
__bool__ |
if obj: |
期望实现:当前未调度 |
__abs__ |
abs(obj) |
期望实现:当前未调度 |
__del__ |
del obj |
期望实现:当前未调度 |
__delete__ |
del obj |
期望实现:当前未调度 |
__radd__ |
右操作数加法 | 尚未调度 |
__rsub__ |
右操作数减法 | 尚未调度 |
__rmul__ |
右操作数乘法 | 尚未调度 |
__rtruediv__ |
右操作数真除法 | 尚未调度 |
__rfloordiv__ |
右操作数整除 | 尚未调度 |
__rmod__ |
右操作数取模 | 尚未调度 |
__rpow__ |
右操作数幂运算 | 尚未调度 |