编程进阶网 编程进阶网
首页
  • 计算机原理
  • 操作系统
  • 网络协议
  • 数据库原理
  • 面向对象
  • 设计原则
  • 设计模式
  • 系统架构
  • 性能优化
  • 编程原理
  • 方案设计
  • 稳定可靠
  • 工程运维
  • 基础认知
  • 线性结构
  • 树与哈希
  • 工业级实现
  • 算法思想
  • 实战与综合
  • 算法题考核
  • C语言入门
  • C综合案例
  • C专栏博客
  • C标准集库
  • C++入门教程
  • C++综合案例
  • C++专栏博客
  • C++开发技巧
  • Java入门教程
  • Java综合案例
  • Java专栏博客
  • Go入门教程
  • Go综合案例
  • Go专栏博客
  • Go开发技巧
  • JavaScript入门
  • JavaScript高级
  • Android库解读
  • Android专栏
  • Android智能硬件
  • iOS ObjC入门
  • iOS Swift入门
  • iOS入门精通
  • Web之Html手册
  • Web之TypeScript
  • Web之Vue高级进阶
  • Linux之QML入门
  • Linux之QT核心库
  • Linux实践开发
  • Python教程
  • Shell&Bash教程
  • 工具脚本
  • 自动化脚本
  • 质量保障
  • 产品思考
  • 软实力
  • 开发流程
  • Git应用
  • 技术模版
  • 技术规范
  • Markdown
  • Mermaid
  • 开源协议
  • JSON工具
  • 文本工具
  • 图片处理
  • 文档转化
  • 代码压缩
  • 关于我
  • 自我精进
  • 职场管理
  • 职场面试
  • 心情杂货
  • 友情链接

杨充

专注编程 · 终身学习者
首页
  • 计算机原理
  • 操作系统
  • 网络协议
  • 数据库原理
  • 面向对象
  • 设计原则
  • 设计模式
  • 系统架构
  • 性能优化
  • 编程原理
  • 方案设计
  • 稳定可靠
  • 工程运维
  • 基础认知
  • 线性结构
  • 树与哈希
  • 工业级实现
  • 算法思想
  • 实战与综合
  • 算法题考核
  • C语言入门
  • C综合案例
  • C专栏博客
  • C标准集库
  • C++入门教程
  • C++综合案例
  • C++专栏博客
  • C++开发技巧
  • Java入门教程
  • Java综合案例
  • Java专栏博客
  • Go入门教程
  • Go综合案例
  • Go专栏博客
  • Go开发技巧
  • JavaScript入门
  • JavaScript高级
  • Android库解读
  • Android专栏
  • Android智能硬件
  • iOS ObjC入门
  • iOS Swift入门
  • iOS入门精通
  • Web之Html手册
  • Web之TypeScript
  • Web之Vue高级进阶
  • Linux之QML入门
  • Linux之QT核心库
  • Linux实践开发
  • Python教程
  • Shell&Bash教程
  • 工具脚本
  • 自动化脚本
  • 质量保障
  • 产品思考
  • 软实力
  • 开发流程
  • Git应用
  • 技术模版
  • 技术规范
  • Markdown
  • Mermaid
  • 开源协议
  • JSON工具
  • 文本工具
  • 图片处理
  • 文档转化
  • 代码压缩
  • 关于我
  • 自我精进
  • 职场管理
  • 职场面试
  • 心情杂货
  • 友情链接
  • ScriptHub 脚本工具箱
  • Python

    • Python 从入门到实战
    • 入门与基础类型
    • 序列与集合类型
    • 流程控制与函数
    • 面向对象与工程
    • 爬虫全流程实战
    • 数据分析三件套
    • 办公自动化实战
    • 开发环境与规范
    • 调试与性能优化
    • 部署与并发实战
    • 函数高级特性剖析:装饰器 / 生成器 / 上下文管理器
    • 并发底层原理揭秘
    • 面向对象与类型系统:元类 / 描述符 / 鸭子类型
      • 13.1 元类的底层机制
        • 13.1.1 类也是对象
        • 13.1.2 _new 与 init_
        • 13.1.3 自定义元类实战
      • 13.2 描述符协议详解
        • 13.2.1 基础描述符
        • 13.2.2 类型校验
      • 13.3 鸭子类型与抽象基类
        • 13.2.3 惰性缓存
        • 13.3.1 鸭子类型
        • 13.3.2 抽象基类
        • 13.3.3 collections.abc
      • 13.4 类型系统全景总结
    • 解释器源码初探
  • Shell-Bash

  • 工具脚本

  • ScriptHub
  • Python
杨充
2023-10-27
目录

面向对象与类型系统:元类 / 描述符 / 鸭子类型

# 第 13 章 面向对象与类型系统

# 目录介绍

  • 13.1 元类的底层机制
    • 13.1.1 类也是对象
    • 13.1.2 new 与 init
    • 13.1.3 自定义元类实战
  • 13.2 描述符协议详解
    • 13.2.1 基础描述符
    • 13.2.2 类型校验
    • 13.2.3 惰性缓存
  • 13.3 鸭子类型与抽象基类
    • 13.3.1 鸭子类型
    • 13.3.2 抽象基类
    • 13.3.3 collections.abc
  • 13.4 类型系统全景总结

Python 的 OOP 比 Java/C++ 更灵活——类本身也是对象、属性访问可定制、类型检查基于行为而非继承。

# 13.1 元类的底层机制

类也是对象——class 语句本质上是调用 type() 创建了一个对象:

# 用 class 语法
class Person:
    name = 'Alice'

# 等价于 type() 调用
Person = type('Person', (), {'name': 'Alice'})
#           ↑类名   ↑基类元组  ↑类属性字典
1
2
3
4
5
6
7

type 既是工厂函数也是元类——它创建了所有类(包括它自己)。

# 13.1.1 类也是对象

class Demo:
    def __new__(cls, *a, **kw):
        print(f"1. __new__ 创建实例,cls={cls.__name__}")
        return super().__new__(cls)   # 必须返回实例!
    def __init__(self):
        print(f"2. __init__ 初始化实例")

Demo()  
# 1. __new__ 创建实例,cls=Demo
# 2. __init__ 初始化实例
1
2
3
4
5
6
7
8
9
10

__new__ 负责分配内存返回实例(静态方法),__init__ 负责设置属性(实例方法)。单例模式就在 __new__ 中实现。

# 13.1.2 new 与 init

元类 = 类的类。当执行 class MyClass(meta=MyMeta): 时,Python 调用 MyMeta.__new__ 创建这个类对象。

#!/usr/bin/env python3
"""用元类实现 ORM 模型——自动收集字段定义"""
import re

class ModelMeta(type):
    def __new__(mcs, name, bases, namespace):
        if name == 'Model':           # 跳过基类
            return super().__new__(mcs, name, bases, namespace)

        # 自动收集字段定义
        namespace['_fields'] = {
            k: v for k, v in namespace.items()
            if isinstance(v, Field)
        }
        namespace.setdefault('_table', name.lower() + 's')
        return super().__new__(mcs, name, bases, namespace)

class Field:
    def __init__(self, field_type, required=False):
        self.type = field_type; self.required = required

class Model(metaclass=ModelMeta):
    def __init__(self, **kwargs):
        for name, field in self._fields.items():
            value = kwargs.get(name)
            if field.required and value is None:
                raise ValueError(f"{name} is required")
            setattr(self, name, value)

# ---- 使用 ----
class User(Model):
    name = Field(str, required=True)
    age = Field(int)

u = User(name='Alice', age=30)
print(u._fields)     # {'name': <Field>, 'age': <Field>}
print(u._table)      # 'users'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

# 13.1.3 自定义元类实战

多继承时,Python 用 C3 线性化算法 计算 MRO——保证子类优先、基类顺序一致:

class A: pass
class B(A): pass
class C(A): pass
class D(B, C): pass

print(D.__mro__)
# (D, B, C, A, object)  ← C3 线序:D→B→C→A→object

# super() 沿 MRO 链向上查找
class E:
    def greet(self): print("E")
class F(E):
    def greet(self): print("F"); super().greet()  # 找 MRO 中 E 之后的类
1
2
3
4
5
6
7
8
9
10
11
12
13

C3 核心规则:L[C] = C + merge(L[B1], ..., L[Bn], B1...Bn)。你不必手算——cls.__mro__ 随时可查。

# 13.2 描述符协议详解

描述符协议 = __get__ / __set__ / __delete__。实现了这些方法的对象可以控制另一个类的属性访问行为。

property 就是内置描述符——它为你包装了 getter/setter/deleter 三个函数。

# 13.2.1 基础描述符

数据描述符 vs 非数据描述符:实现了 __set__ 的描述符叫"数据描述符"(如 property),优先级高于实例字典;只有 __get__ 的是"非数据描述符",实例字典优先级更高。

#!/usr/bin/env python3
"""描述符——类型校验器"""
class Typed:
    def __init__(self, name, expected_type):
        self.name = name; self.type = expected_type
        self.data = {}               # 属主实例→值 映射

    def __get__(self, obj, objtype=None):
        if obj is None: return self  # 类级别访问返回描述符本身
        return self.data.get(obj)

    def __set__(self, obj, value):
        if not isinstance(value, self.type):
            raise TypeError(f"{self.name} must be {self.type.__name__}")
        self.data[obj] = value

class Person:
    name = Typed('name', str)
    age  = Typed('age', int)

p = Person()
p.name = 'Alice'      # ✅
# p.age = '30'        # ❌ TypeError
p.age = 30            # ✅
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# 13.2.2 类型校验

#!/usr/bin/env python3
"""描述符实现惰性缓存——只计算一次"""
class CachedProperty:
    def __init__(self, func):
        self.func = func
        self.name = func.__name__

    def __get__(self, obj, objtype=None):
        if obj is None: return self
        value = self.func(obj)
        # 关键:把结果存在实例 __dict__ 中——
        # 下次访问时实例属性优先于描述符(非数据描述符的情况)
        obj.__dict__[self.name] = value
        return value

class Heavy:
    @CachedProperty
    def data(self):
        print("  [computing...]")
        import time; time.sleep(1)
        return sum(range(10**6))

h = Heavy()
print(h.data)   # [computing...] → 499999500000
print(h.data)   # 499999500000  ← 直接返回,无计算
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

数据描述符 vs 非数据描述符:实现了 __set__ 的描述符叫"数据描述符"(如 property),优先级高于实例字典;只有 __get__ 的是"非数据描述符"(如 classmethod),实例字典优先级更高——这就是 cached_property 能工作的原因。

# 13.3 鸭子类型与抽象基类

鸭子类型:"如果它走路像鸭子、叫声像鸭子,那它就是鸭子"——只要对象有需要的属性和方法,Python 就接受它,不需要显式继承。

抽象基类(ABC):反过来——显式声明接口,调用方可以依赖 isinstance 检查。

# 13.2.3 惰性缓存

# 不要求继承任何基类——只要有 read() 就能用
def process(reader):
    return reader.read().upper()

# 文件对象
with open('test.txt') as f:
    print(process(f))          # ✅

# 内存字符串——只要 wrap 一个类
class StringReader:
    def __init__(self, data): self.data = data
    def read(self): return self.data

print(process(StringReader('hello')))  # ✅ 鸭子类型!
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 13.3.1 鸭子类型

import abc

class AbstractCache(abc.ABC):
    @abc.abstractmethod
    def get(self, key): ...
    @abc.abstractmethod
    def set(self, key, value): ...

class RedisCache(AbstractCache):   # ❌ 忘记实现 get/set → TypeError
    pass

# 使用 ABC 的好处:ide 提示、文档约束、isinstance 检查
assert issubclass(RedisCache, AbstractCache)  # 如果实现了抽象方法
1
2
3
4
5
6
7
8
9
10
11
12
13

# 13.3.2 抽象基类

Protocol 让鸭子类型获得静态类型检查的支持:

from typing import Protocol

class SupportsClose(Protocol):
    def close(self) -> None: ...

def cleanup(resource: SupportsClose) -> None:
    resource.close()

# 不需要继承 SupportsClose——只要有 close() 方法就能通过 mypy 检查
class MyResource:
    def close(self): print("closed")

cleanup(MyResource())  # ✅ mypy 不会报错
1
2
3
4
5
6
7
8
9
10
11
12
13

# 13.3.3 collections.abc

from collections.abc import MutableSequence

# 只要实现 5 个抽象方法就能获取完整的 list 接口!
class MyList(MutableSequence):
    def __init__(self): self._data = []
    def __len__(self): return len(self._data)
    def __getitem__(self, i): return self._data[i]
    def __setitem__(self, i, v): self._data[i] = v
    def __delitem__(self, i): del self._data[i]
    def insert(self, i, v): self._data.insert(i, v)

ml = MyList(); ml.append(1); ml.extend([2,3])
print(ml.count(1))       # 0 — count 由 ABC 自动提供
print(isinstance(ml, MutableSequence))  # True
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 13.4 类型系统全景总结

机制 检查时机 语法 场景
元类 类定义时 class X(meta=M) 框架/ORM
描述符 属性访问时 __get__/__set__ property/类型校验
鸭子类型 运行时 无——纯约定 Python 默认风格
ABC 实例化/isinstance @abstractmethod 框架接口定义
Protocol 静态检查(mypy) class X(Protocol) 结构子类型
多重继承+MRO 方法调用时 class D(B,C) Mixin 复用
super() 方法调用时 super().method() 沿 MRO 链协作

选择建议:优先鸭子类型;需要强制接口时用 ABC;需要静态检查时用 Protocol;框架/元编程才碰元类。

#Python#原理
上次更新: 2026/06/17, 12:47:39
并发底层原理揭秘
解释器源码初探

← 并发底层原理揭秘 解释器源码初探→

最近更新
01
信号崩溃快速排查
06-15
02
CoreDump破案
06-15
03
perf火焰图实战
06-15
更多文章>
Theme by Vdoing | Copyright © 2019-2026 杨充 | MIT License | 桂ICP备2024034950号 | 桂公网安备45142202000030
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式