840 lines
27 KiB
Markdown
840 lines
27 KiB
Markdown
# 06 - 面向对象与运算符重载
|
||
|
||
Viper 中的 `class` 有两种语义:**数据布局**(结构体/联合体/枚举,见 [05-classes.md](05-classes.md))和**面向对象**。OOP 能力由**类内是否存在方法**自动判定(`StructEntry.IsOOP`);多态用 `virtual`,非多态继承(C++ 风格,无 vtable 开销)用 `novirtual`。
|
||
|
||
> `@t.Object` 装饰器默认**不需要写、也尽量避免使用**——OOP 由「类内是否有方法」自动判定,写与不写行为一致。这是本文唯一一次提及,新代码请直接省略。
|
||
|
||
## 类修饰符(`virtual` / `novirtual`)
|
||
|
||
`virtual` / `novirtual` 是前置软关键字,**类级与方法级都可用**:
|
||
|
||
```python
|
||
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 行为。
|
||
|
||
### 基本定义
|
||
|
||
```python
|
||
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` 也不失为一种办法。
|
||
|
||
### 成员方法
|
||
|
||
```python
|
||
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
|
||
```
|
||
|
||
### 方法调用
|
||
|
||
```python
|
||
sheet: Sheet = Sheet(0, 0, 640, 480)
|
||
sheet.MoveTo(100, 100)
|
||
sheet.Resize(800, 600)
|
||
```
|
||
|
||
### 属性装饰器
|
||
|
||
支持 Python 的 `@property` 和 `@xxx.setter` 装饰器:
|
||
|
||
> `@xxx.setter` 和自定义装饰器正在试验阶段,尽可能不要使用
|
||
|
||
```python
|
||
class MyObj:
|
||
_value: int
|
||
|
||
@property
|
||
def value(self) -> int:
|
||
return self._value
|
||
|
||
@value.setter
|
||
def value(self, v: int):
|
||
self._value = v
|
||
```
|
||
|
||
### 静态方法
|
||
|
||
```python
|
||
@staticmethod
|
||
def _yield():
|
||
pass
|
||
```
|
||
|
||
### 对象指针与解引用
|
||
|
||
对象类实例通常通过指针操作:
|
||
|
||
```python
|
||
obj_ptr: MyClass* = &MyClass()
|
||
obj: MyClass = *obj_ptr
|
||
```
|
||
|
||
### 成员访问
|
||
|
||
通过 `self` 访问成员变量和方法:
|
||
|
||
```python
|
||
def method(self) -> void:
|
||
self.x = 10
|
||
y: int = self.y
|
||
self.DoSomething()
|
||
```
|
||
|
||
### 内嵌对象
|
||
|
||
对象类可以包含其他对象实例(非指针):
|
||
|
||
```python
|
||
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:
|
||
|
||
```llvm
|
||
define void @"<SHA1>.ClassName.__before_init__"(%"<SHA1>.ClassName"* %self) {
|
||
entry:
|
||
; 对 virtual 类:存储 vtable 指针到 slot 0
|
||
; 遍历所有成员:若有默认值则 store 默认值,否则 store 零值
|
||
ret void
|
||
}
|
||
```
|
||
|
||
**职责**:
|
||
- 填充类定义中声明的成员默认值(如 `x: int = 0` 中的 `0`)
|
||
- 对无默认值的成员填充类型零值
|
||
- 对 `virtual` 类额外初始化 vtable 指针
|
||
|
||
**调用时机**(由编译器在构造时自动插入):
|
||
1. 不存在 `__new__` 时:在 `alloca` 分配之后调用一次,随后进入 `__init__`
|
||
2. 存在 `__new__` 时:在 `__new__` 之后对**最终存储指针**调用一次,随后进入 `__init__`
|
||
|
||
**关于 `__new__`**:
|
||
- `__new__` 返回 `self` 时使用默认的 `alloca` 存储
|
||
- `__new__` 的签名可与 `__init__` 不同
|
||
- `__new__` 与 `virtual` 兼容
|
||
- `__new__` 本身不入虚表
|
||
|
||
**就地构造**:用户也可手动调用 `__before_init__` 实现就地构造(placement new 语义),典型用法见 `mpool.alloc_buf`:
|
||
|
||
```python
|
||
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 之后) |
|
||
| 运行时开销 | 零 | 每次虚调用一次间接跳转 |
|
||
|
||
### 基本定义
|
||
|
||
```python
|
||
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](14-multiple-inheritance.md)。
|
||
|
||
### 跨模块继承
|
||
|
||
跨模块父类(如 `linkedlist.LinkedNode`)的 `novirtual` 标记会在导入时被检测并记录,子类继承时同样跳过 vtable 插入。
|
||
|
||
### 向下转型
|
||
|
||
父类指针→子类指针需显式转型(生成 bitcast IR):
|
||
|
||
```python
|
||
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 行为由「结构体内是否有函数」自动判定)。
|
||
|
||
### 基本定义
|
||
|
||
```python
|
||
virtual class Base:
|
||
def __init__(self):
|
||
pass
|
||
def method(self) -> int:
|
||
return 0
|
||
```
|
||
|
||
在 LLVM IR 中,`virtual` 类的结构体类型自动插入 vtable 指针:
|
||
|
||
```llvm
|
||
%"<SHA1>.Base" = type { i8*, i32 }
|
||
```
|
||
|
||
同时,编译器会生成全局 vtable 变量。vtable 实为 `internal constant struct{ n x i8* }`——即含 n 个 `i8*` 函数指针成员的结构体常量(成员为带 `bitcast` 的初始化文本),变量名为 `<sha1>.Class.vtable`,并非 `[1 x i8*] zeroinitializer`:
|
||
|
||
```llvm
|
||
@"<sha1>.Base.vtable" = internal constant { i8* } {
|
||
i8* bitcast (i32 (%"<sha1>.Base"*)* @"<sha1>.Base.method" to i8*)
|
||
}
|
||
```
|
||
|
||
### 继承与多态
|
||
|
||
虚表可以帮你使用继承语法实现多态:
|
||
|
||
```python
|
||
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` 结构体,还可以使用:
|
||
|
||
```python
|
||
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),避免修改影响所有实例。
|
||
|
||
### 虚方法调度机制
|
||
|
||
虚方法调用的核心流程:
|
||
|
||
1. 从对象指针 GEP 获取第 0 个 slot(vtable 指针)
|
||
2. 加载 vtable 指针
|
||
3. bitcast 为 `[N x i8*]*` 类型
|
||
4. GEP 获取方法索引处的函数指针
|
||
5. 加载函数指针
|
||
6. bitcast 为正确的函数指针类型
|
||
7. 间接调用
|
||
|
||
### 方法级虚表开关
|
||
|
||
类级 `virtual` / `novirtual` 决定**该类所有方法的默认级别**;要单独改变**某一个方法**,就在该方法上加相反的方法级修饰符:
|
||
|
||
- `novirtual` 类中单独标注 `virtual def` 的方法进入虚表;
|
||
- `virtual` 类中单独标注 `novirtual def` 的方法被排除在虚表之外。
|
||
|
||
```python
|
||
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 动态派发:
|
||
|
||
```python
|
||
virtual class Base:
|
||
def method(self) -> void:
|
||
pass
|
||
|
||
virtual class Derived(Base):
|
||
def method(self) -> void:
|
||
super().method() # 静态调用父类实现
|
||
```
|
||
|
||
## 继承
|
||
|
||
### 结构体成员继承
|
||
|
||
当子类继承父类(父类在 `Gen.class_members` 中存在)时:
|
||
|
||
- 父类的成员变量被继承到子类(排在子类成员之前)
|
||
- 父类的默认值也被继承
|
||
- 父类的方法被继承(重命名为 `子类名.方法名`)
|
||
|
||
```python
|
||
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` 的异常匹配:
|
||
|
||
```python
|
||
class MyError(Exception):
|
||
pass
|
||
|
||
class SpecificError(MyError):
|
||
pass
|
||
```
|
||
|
||
详见 [09-exceptions.md](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__` 重载。
|
||
|
||
示例:
|
||
|
||
```python
|
||
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)`(两端已调度)。
|
||
|
||
```python
|
||
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__` — 读取
|
||
|
||
```python
|
||
def __getitem__(self, key: int) -> int:
|
||
return self.data[key]
|
||
```
|
||
|
||
### `__setitem__` — 赋值
|
||
|
||
```python
|
||
def __setitem__(self, key: int, value: int):
|
||
self.data[key] = value
|
||
```
|
||
|
||
### `__delitem__` — 删除
|
||
|
||
```python
|
||
def __delitem__(self, key: int):
|
||
pass
|
||
```
|
||
|
||
## 布尔转换 `__bool__`
|
||
|
||
对象类定义了 `__bool__` 时,条件位置自动调度(两端已调度):`if`/`elif`/`while` 条件、`and`/`or`/`not` 操作数、三元表达式条件。
|
||
|
||
```python
|
||
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__`
|
||
|
||
```python
|
||
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*`)输出(两端已调度)。
|
||
|
||
```python
|
||
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](11-builtins.md)。
|
||
|
||
```python
|
||
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__`
|
||
|
||
```python
|
||
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 循环使用
|
||
|
||
```python
|
||
r: IntRange = IntRange(0, 10)
|
||
for i in r:
|
||
print(i)
|
||
```
|
||
|
||
编译器生成的等价逻辑:
|
||
|
||
```c
|
||
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__` 方法:
|
||
|
||
```python
|
||
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)
|
||
```
|
||
|
||
使用方式:
|
||
|
||
```python
|
||
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)` |
|
||
|
||
```python
|
||
del obj # 调用 obj.__delete__()
|
||
del arr[idx] # 调用 arr.__delitem__(idx)
|
||
```
|
||
|
||
## `isinstance`
|
||
|
||
`isinstance(obj, Cls)` 不是关键字,而是编译器在调用点按函数名识别的内建(返回 `i1`)。
|
||
|
||
```python
|
||
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](../includes/_fakeduck.vp):`_Type.type_id()`;`_str` 提供 `upper` / `lower` / `capitalize` / `swapcase` / `title` / `casefold` / `isupper` / `islower` / `isalpha` / `isdigit` / `isalnum` / `isspace` / `isascii` / `istitle` / `split` 等。
|
||
|
||
### 扩展方法调用
|
||
|
||
```python
|
||
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__`):
|
||
|
||
```python
|
||
a.__mbuddy__ = bd # 语法糖,等价 a.set_mbuddy(bd)
|
||
d: str = a.__data__ # 读影子字段
|
||
```
|
||
|
||
`with` 上下文可**自动注入**字段:提供方类用类体注解 `__provides__ = ['字段名']` 声明,消费方扩展类用 `__requires__ = ['字段名']` 声明:
|
||
|
||
```python
|
||
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](../includes/_withcontent.vp)),未 import `_withcontent` 时跳过注入。
|
||
|
||
真实用例:[Test/FakeDuckTest](../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__` | 右操作数幂运算 | 尚未调度 |
|