下面是一篇可直接发布的技术博文草稿,面向有一定 Python 基础、希望深入理解类型系统的读者。


深入理解 Python typing.Final

在使用 Python 编写大型项目时,我们经常希望某些变量 只被赋值一次,或者某些方法 不允许在子类中被重写
Python 的 typing 模块提供了一个重要工具:Final

本文将介绍:

  • Final 是什么
  • Final 的两种核心用途
  • 常见使用场景
  • 与常量的区别
  • 实际项目中的最佳实践

1 什么是 Final

Final 是 Python 类型系统中的一个 类型限定符(type qualifier),用于告诉类型检查器:

这个变量或属性 不应该被重新赋值或被子类覆盖

它最早在 PEP 591 中被提出,并在 Python 3.8 中正式加入 typing 模块。

需要注意:

  • Final 不会在运行时强制限制
  • 它只对 类型检查工具(例如 mypypyright)生效

示例:

from typing import Final

API_VERSION: Final = "v1"

这意味着:

  • API_VERSION 应该只赋值一次
  • 之后不应该再修改

2 Final 的两种主要用途

Final 主要有 两个核心用途

1️⃣ 声明常量
2️⃣ 防止子类重写属性


3 用法一:声明常量

这是 Final 最常见的使用方式

from typing import Final

MAX_CONNECTIONS: Final = 10

如果之后写:

MAX_CONNECTIONS = 20

类型检查工具会报错:

Cannot assign to final name "MAX_CONNECTIONS"

为什么不用普通变量?

传统写法:

MAX_CONNECTIONS = 10

Python 并不会阻止:

MAX_CONNECTIONS = 20

使用 Final 后,类型检查器就能帮助发现问题。


4 用法二:防止子类重写

Final 还可以用于 类属性,防止子类修改。

示例:

from typing import Final

class BaseConfig:
    VERSION: Final = "1.0"

子类中如果尝试:

class DevConfig(BaseConfig):
    VERSION = "2.0"

类型检查器会提示:

Cannot override final attribute "VERSION"

5 Final 与常量命名规范的区别

在 Python 中,传统的“常量”只是 命名约定

API_URL = "https://api.example.com"

全大写只是告诉开发者:

“请不要修改它”

但语言本身 不会限制

Final 提供的是:

方式 是否强制 类型检查
命名规范
Final 否(运行时) 是(静态检查)

因此:

最佳实践是同时使用两者

API_URL: Final = "https://api.example.com"

6 类属性中的 Final

一个常见场景是 类常量

from typing import Final

class HttpStatus:
    OK: Final = 200
    NOT_FOUND: Final = 404

这样:

  • 这些值不会被意外修改
  • 子类也不会覆盖

7 Final 与实例变量

Final 也可以用于 实例属性

from typing import Final

class User:
    id: Final[int]

    def __init__(self, id: int):
        self.id = id

这里表示:

  • id 只能在 __init__ 中赋值
  • 之后不应该再修改

例如:

user.id = 5

类型检查器会报错。


8 Final 不会在运行时生效

这是很多人容易误解的一点。

即使写了:

API_VERSION: Final = "v1"
API_VERSION = "v2"

Python 仍然可以运行

Final 只是帮助:

  • mypy
  • pyright
  • IDE(如 PyCharm、VSCode)

进行 静态分析


9 Final 的典型使用场景

在真实项目中,Final 常见于:

1 配置常量

TIMEOUT: Final = 30

2 API 版本

API_VERSION: Final = "v1"

3 HTTP 状态码

class Status:
    OK: Final = 200

4 不希望被子类修改的属性

class BaseService:
    NAME: Final = "base"

10 最佳实践

推荐的实践方式:

1 常量同时使用 Final + 大写

DATABASE_URL: Final = "postgres://..."

2 用于不应该改变的配置

例如:

  • 端口
  • API 版本
  • 默认路径

3 在大型项目中使用

Final 在以下情况下最有价值:

  • 团队协作项目
  • 大型代码库
  • 公共库 API

结语

Final 虽然只是 Python 类型系统中的一个小特性,但在 提高代码可维护性和可读性方面非常有价值。

总结:

  • Final 用于声明 不可重新赋值的变量
  • 可以防止 子类覆盖属性
  • 只在 静态类型检查阶段生效
  • 推荐与 常量命名规范一起使用

如果你的项目已经开始使用类型检查工具,那么 Final 是一个非常值得引入的工具。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐