Python 中的常量类型 | Python 中常量的重要性

2024年8月29日 | 阅读 10 分钟

在本教程中,我们将学习常量类型以及它们如何帮助提高代码可读性。如果您不熟悉,常量是表示在程序执行期间不会更改的值的名称。它们是编程中最常见的基本概念。然而,Python 没有专门的语法来定义常量。通常,Python 常量是永不改变的变量。我们将在接下来的部分中详细讨论 Python 常量。

什么是常量?

通常,常量项用于数学中,表示一个永不改变的值或数量。在编程中,常量是指与在程序执行期间永不改变的值相关联的名称。编程常量与其他常量不同,它由两部分组成——一个名称和一个关联的值。名称将描述常量的全部内容,而值是常量本身的具体表达。

一旦我们定义了常量,我们只能访问它的值,但不能随时间更改它。但是,我们可以修改变量的值。一个现实生活中的例子是——光速、一小时中的分钟数以及项目根文件夹的名称。

为什么要使用常量?

在编程语言中,常量可以防止我们意外更改其值,这可能会导致难以调试的错误。它还有助于使代码更具可读性和可维护性。让我们看看常量的一些优点。

  • 提高可读性 - 它有助于提高代码的可读性。例如,读取和理解名为 MAX_SPEED 的常量比读取和理解实际的速度值更容易。
  • 清晰地传达意图 - 大多数开发人员将 3.14 视为 pi 常量。然而,Pi、pi 或 PI 名称将更清晰地传达意图。这种做法将使其他开发人员能够理解我们的代码。
  • 更好的可维护性 - 常量允许我们在整个代码中使用相同的值。假设我们要更新常量的值;我们不需要更改每个实例。
  • 错误风险低 - 在整个程序中代表给定值的常量不易出错。如果我们要更改计算中的精度,替换值可能存在风险。我们可以创建不同精度级别的不同常量,并在需要的地方更改代码,而不是替换它。
  • 线程安全数据存储 - 常量是线程安全的对象,这意味着多个线程可以同时使用一个常量而不会丢失数据。

用户定义的常量

我们需要使用 Python 中的命名约定来定义 Python 中的常量。我们应该用大写字母书写名称,单词之间用下划线分隔。

以下是用户定义的 Python 常量的示例 -

我们使用与在 Python 中创建变量相同的方式。因此,我们可以假设 Python 常量只是变量,唯一的区别是常量只使用大写字母。

使用大写字母使常量在我们的变量中脱颖而出,这是一种有用或首选的做法。

上面我们讨论了用户定义的常量;Python 还提供了几个可以考虑并应视为常量的内部名称。

Python 中的重要常量

在本节中,我们将学习一些用于使 Python 代码更具可读性的内部常量。让我们了解一些重要的常量。

内置常量

在官方文档中,TrueFalse 被列为第一个常量。它们是 Python 布尔值,是 int 的实例。True 的值为 1,而 False 的值为 0。

示例 -

请记住,True 和 False 名称是严格的常量。换句话说,我们不能重新分配它们,如果我们尝试重新分配它们,我们将得到一个语法错误。这两个值是 Python 中的单例对象,这意味着只存在一个实例。

内部双下划线名称

Python 还有许多我们可以视为常量的内部双下划线名称。有几个这样的唯一名称,我们将在本节中了解 __name__ 和 __file__。

__name__ 属性与如何运行给定的代码片段有关。当导入模块时,Python 内部将 __name__ 设置为包含模块名称的字符串。

new_file.py

在命令行中输入以下命令 -

-c 用于在命令行执行一小段 Python 代码。在上面的示例中,我们导入了 new_file 模块,它在屏幕上显示一些消息。

输出 -

The type of __name__ is: <class 'str'>
The value of __name__ is: timezone

正如我们所看到的,__name__ 存储了 __main__ 字符串,表示我们可以直接将可执行文件作为 Python 程序运行。

另一方面,__file__ 属性包含 Python 当前正在导入或执行的文件。如果我们在文件内部使用 __file__ 属性,我们将获得模块本身的路径。

让我们看下面的例子 -

示例 -

输出

The type of __file__ is: <class 'str'>
The value of __file__ is: D:\Python Project\new_file.py 

我们也可以直接运行并得到相同的结果。

示例 -

输出

python new_file.py
The type of __file__ is: <class 'str'>
The value of __file__ is: timezone.py

有用的字符串和数学常量

标准库中有许多有价值的常量。有些严格连接到特定的模块、函数和类;许多是通用的,我们可以在多种场景中使用它们。在下面的示例中,我们将分别使用数学和字符串相关的模块 math 和 string。

让我们理解以下示例 -

示例 -

当我们编写数学相关的代码或执行一些特定计算时,这些常量将发挥至关重要的作用。

让我们理解以下示例 -

示例 -

在上面的代码中,我们使用了 math.pi 而不是自定义的 PI 常量。数学相关的常量为程序提供了更多的上下文。使用 math.pi 常量的优点是,如果我们使用的是 Python 的旧版本,我们将获得 Pi 的 32 位版本。如果我们在现代 Python 版本中使用上述程序,我们将获得 Pi 的 64 位版本。因此,我们的程序将根据其具体的执行环境进行自我调整。

string 模块还提供了一些有用的内置字符串常量。下面是每个常量的名称和值的表格。

名称
ascii_lowercaseabcdefghijklmnopqrstuvwxyz
ascii_uppercaseABCDEFGHIJKLMNOPQRSTUVWXYZ
ascii_lettersABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
digits0123456789
hexdigits0123456789abcdefABCDEF
octdigits01234567

我们可以将这些字符串相关的常量用于正则表达式、自然语言处理、大量的字符串处理等等。

类型标注常量

从 Python 3.8 开始,typing 模块包含一个 Final 类,允许我们类型标注常量。如果我们在程序中使用 Final 类来定义常量,我们将得到一个静态类型错误,mypy 检查器会检查它,并显示我们不能重新分配给 Final 名称。让我们理解下面的例子。

示例 -

我们用 Final 类指定了常量变量,该类指示类型错误报告错误,如果声明的名称被重新分配。然而,它会收到一个类型检查器的错误报告;Python 会更改 MAX_SPEED 的值。因此,Final 不会在运行时阻止常量意外重新分配。

字符串常量

正如前面部分所讨论的,Python 不支持严格的常量;它只有永不更改的变量。因此,Python 社区遵循使用大写字母来标识常量变量的命名约定。

如果我们在一个有许多不同级别程序员的大型 Python 项目中工作,这可能是一个问题。因此,拥有一个允许我们使用严格常量的机制将是一个好习惯。正如我们所知,Python 是一种动态语言,有几种方法可以使常量不可更改。在本节中,我们将学习其中一些方法。

.__slots__ 属性

Python 类提供了使用 __slots__ 属性的功能。插槽具有特殊的机制来减小对象的大小。它是对象内存优化的一种概念。如果我们在类中使用 __slots__ 属性,我们将无法添加新的实例,因为它不使用 __dict__ 属性。此外,没有 .__dict__ 属性意味着在内存消耗方面的优化。让我们理解下面的例子。

示例 - 不使用 __slots__ 属性

输出 -

{'a': 1, 'b': 2}

Python 中的每个对象都包含一个允许添加属性的动态字典。字典消耗大量内存,使用 __slots__ 可以减少空间和内存的浪费。让我们看另一个例子。

示例 -

输出 -

3.141592653589793
2.718281828459045
Traceback (most recent call last):
  File "<string>", line 10, in <module>
AttributeError: 'ConstantsName' object attribute 'PI' is read-only

在上面的代码中,我们使用 slots 属性初始化了类属性。变量具有常量值,如果尝试重新分配变量,将得到一个错误。

@property 装饰器

我们还可以使用 @property 装饰器来创建一个充当常量命名空间的类。我们只需要定义常量属性,而不为它们提供设置器方法。让我们理解下面的例子。

示例 -

输出 -

3.141592653589793
2.718281828459045
Traceback (most recent call last):
  File "<string>", line 13, in <module>
AttributeError: can't set attribute

它们只是只读属性,如果尝试重新分配,将得到一个 AttributeError。

namedtuple() 工厂函数

Python 的 collection 模块带有名为 namedtuple() 的工厂函数。使用 namedtuple() 函数,我们可以使用命名字段和点表示法来访问它们的项。我们知道元组是不可变的,这意味着我们不能就地修改现有的命名元组对象。

让我们理解下面的例子。

示例 -

输出 -

3.141592653589793
2.718281828459045
Traceback (most recent call last):
  File "<string>", line 17, in <module>
AttributeError: can't set attribute

@dataclass 装饰器

顾名思义,数据类持有数据,它们可以包含方法,但这不是它们的主要目标。我们需要使用 @dataclass 装饰器来创建数据类。我们还可以创建严格的常量。@dataclass 装饰器接受一个 frozen 参数,允许我们将数据类标记为不可变。使用 @dataclass 装饰器的优点是,我们不能修改其实例属性。

让我们理解下面的例子。

示例 -

输出 -

3.141592653589793
2.718281828459045
Traceback (most recent call last):
  File "<string>", line 19, in <module>
File "<string>", line 4, in __setattr__
dataclasses.FrozenInstanceError: cannot assign to field 'PI'

解释 -

在上面的代码中,我们导入了 @dataclass 装饰器。我们使用这个装饰器将 ConstantsName 转换为数据类。我们将 frozen 参数设置为 True,使数据类不可变。我们创建了数据类的实例,我们可以访问所有常量但不能修改它们。

.__setattr__() 特殊方法

Python 允许我们使用一个名为 .__setattr__() 的特殊方法。使用此方法,我们可以自定义属性赋值过程,因为 Python 会在每次属性赋值时自动调用此方法。让我们理解下面的例子 -

示例 -

输出 -

3.141592653589793
2.718281828459045
Traceback (most recent call last):
  File "<string>", line 22, in <module>
File "<string>", line 17, in __setattr__
AttributeError: can't reassign constant 'PI'

__setattr__() 方法不允许对类的属性执行任何赋值操作。如果尝试重新赋值,它只会引发一个 AttributeError。

结论

常量是编程中,尤其是数学术语中,最常用的概念。在本教程中,我们学习了常量的概念及其类型。Python 社区使用大写字母作为命名约定来标识常量。但是,我们讨论了一些在 Python 中使用常量的高级方法。我们讨论了如何通过常量提高代码的可读性、可重用性和可维护性。我们提到了如何应用各种技术使我们的 Python 常量严格为常量。