python 3.8 的 typing.final 和 @final 仅作用于类型检查器(如 mypy),运行时完全不阻止继承;若需真正禁止,须在 init_subclass 中主动抛异常。

Python 3.8 里没有 Final 修饰符能阻止类被继承
直接说结论:Python 3.8 的 typing.Final **只对类型检查器(如 mypy)起作用**,运行时完全不拦截继承行为。你写 class Base: ... 加 __final__ = True 或用 @final 装饰器(来自 typing),Python 解释器本身会照常允许子类化——不会报错,也不会警告。
@final 装饰器只在静态检查时生效
从 Python 3.8 开始,typing.final 提供了 @final 装饰器,但它本质是给类型检查工具打标记,不是运行时防护:
-
@final修饰类、方法或变量后,mypy 会报error: Cannot inherit from final class - 但直接运行
python script.py,子类照样能定义、能实例化 - 它不修改
__mro__、不重写__subclasses__、也不拦截__new__ - 示例:
from typing import final <p>@final class Sealed: pass</p><p>class Child(Sealed): # mypy 报错,但 python 解释器完全不管 pass</p>
真想阻止继承?得靠运行时机制
如果业务逻辑确实要求“禁止继承”,必须手动干预运行时行为。常见做法有:
- 在基类
__init_subclass__中主动抛异常:raise TypeError("Sealed class cannot be subclassed") - 注意:Python 3.6+ 才支持
__init_subclass__,且它只在子类定义时触发,不影响已有子类 - 不能依赖
__new__或__init__,因为它们发生在实例化阶段,继承动作早已完成 - 示例:
class Sealed: def __init_subclass__(cls, **kwargs): raise TypeError(f"{cls.__name__} is sealed and cannot be inherited from") <h1>class BadChild(Sealed): # 运行时报 TypeError</h1>混用
@final和__init_subclass__是合理组合两者定位不同,配合使用才完整:
-
@final告诉协作者“不该继承”,并让 mypy 在编辑/CI 阶段就发现问题 -
__init_subclass__是兜底,确保运行时不被绕过(比如有人关掉类型检查) - 注意:
@final不影响__init_subclass__的执行;两者可共存,无冲突 - 别误以为加了
@final就安全了——没配运行时检查,生产环境照样被继承
真正难的不是加个装饰器,而是想清楚:你到底要防谁?是防误用、防协作疏忽,还是防恶意绕过?前者靠类型提示 + 文档,后者只能靠运行时硬拦截,而且得测试覆盖到导入、reload、动态生成类等边界场景。
-
- 在基类
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











