【第 007 讲】Python 数据类型基础:变量与对象 | 数值类型 | 类型判断 | 字符串类型 | 字符串驻留
1 数据类型的基本概念
在 Python 中,程序运行时的所有数据均以 “对象” 的形式存放在计算机内存中。编写程序的过程,本质上就是创建、操作和销毁这些对象的过程。为了准确操作数据,首先需要理解变量与对象之间的关联方式,以及 Python 如何对数据进行类别划分。
1.1 变量与对象
许多编程语言(如 C 或 Java)在定义变量时,需要在变量名前面明确声明数据类型。例如,在 C 语言中声明 int a = 1000; 表示变量 a 被限定为 int 类型,只能存储该类型取值范围内的整数。然而,在 Python 中,定义变量时无需添加类型声明,直接编写 a = 1000 即可。
出现这种差异的根本原因在于:Python 变量本身不具有类型。变量仅扮演 “标签” 或 “引用” 的角色,用于指向内存中的某个对象。真正携带类型信息的,是变量所指向的内存对象本身。
当执行 a = 1000 这行代码时,Python 解释器会在内存中创建一个整数对象 1000。该对象内部存储着两个关键信息:
- 数据值:即 1000。
- 数据类型标识:标识该对象是一个整数。
随后,解释器将变量名 a 与该对象进行绑定。若之后继续执行 a = 2000,解释器则会创建一个新的整数对象 2000,并将变量 a 重新绑定到这个整数对象上。原先的整数对象 1000 若没有任何变量引用,则会被 Python 的垃圾回收机制自动清理。
💡 提示:动态类型
这种 “变量无需声明类型、类型信息由对象自身持有” 的机制,被称为动态类型(Dynamic Typing)。它是 Python 编程灵活性的核心来源。
1.2 数据类型的分类
Python 内置了多种数据类型,用于处理不同种类的信息。依据其用途与存储结构的差异,Python 官方将其划分为以下几个主要类别:
| 类别 | 包含的具体类型 | 简要描述 |
|---|---|---|
| 数值类型 | int(整数)、float(浮点数)、complex(复数)、bool(布尔值) | 表示各种数字,以及真与假两种状态。 |
| 序列类型 | str(字符串)、list(列表)、tuple(元组) | 表示一串有序排列的数据。 |
| 映射类型 | dict(字典) | 表示通过 “键” 来查找对应 “值” 的数据。 |
| 集合类型 | set(集合)、frozenset(不可变集合) | 表示一组互不重复的数据。 |
| 空值类型 | NoneType | 表示 “空” 或 “没有值” 的状态。 |
在本篇文章中,将重点讲解数值类型中的 int(整数)、float(浮点数)、bool(布尔值),以及序列类型中的 str(字符串)。其余类型将在后续专栏文章中逐步展开。
1.3 可变对象与不可变对象
根据对象在创建后其内部值能否被改变,Python 中的对象可分为两种类别。
- 不可变对象:对象创建后,其内存中存储的值不允许被修改。任何试图改变对象值的操作,都会导致 Python 在内存中创建一个新的对象。
- 可变对象:对象创建后,其内部值可以原地修改,修改过程中对象本身的内存地址保持不变。
在当前涉及的数据类型范围内:
- 不可变类型包括:int、float、complex、bool、str、tuple、frozenset。
- 可变类型包括:list、dict、set。
为了直观观察对象在内存中的变化,这里引入 id() 函数。id() 是 Python 的内置函数,用于返回对象在内存中的唯一标识符。在 CPython 解释器中,该标识符即为对象的内存地址。通过比较两次调用 id() 的输出结果,可以判断变量是否指向同一个对象。
我们继续在项目 Python_DS 下新建一个目录,命名为 02_DataTypes。
请看以下示例:
# 01_variable_rebinding.py
# 演示变量重新绑定与不可变对象的关系
a = 1000 # 创建整数对象 1000,变量 a 指向该对象
print(id(a)) # 输出 a 当前指向的内存地址
a = 2000 # 创建新的整数对象 2000,变量 a 重新指向该新对象
print(id(a)) # 输出新的内存地址
程序运行结果如下所示:

如输出所示,两次输出的内存地址不同。这一现象清晰地说明了整数类型的不可变特性:当执行 a = 2000 时,Python 并没有将原来对象 1000 内部的值修改为 2000(因为整数对象禁止修改其值)。正确的执行流程是:在内存的另一个位置新建一个值为 2000 的整数对象,然后将变量 a 的引用从旧对象 1000 切换到新对象 2000。原先的对象 1000 若没有其他变量引用,会被 Python 的垃圾回收机制自动清理。
初学者容易产生一个误解:认为 “变量 a 从 1000 变成了 2000” 意味着对象本身被修改了。这是不准确的。不可变指的是对象本身的值恒定不变,而非变量不能重新指向其他对象。变量可以被随时重新绑定到任意对象,这与对象的可变性是完全独立的两个概念。
2 数值类型概述
在 Python 中,整数、浮点数、复数和布尔值被统一归类为数值类型(Numeric Types)。这四种类型均与数学意义上的数值相关,并且共享一系列通用的运算能力,例如四则运算、比较运算和类型转换。
数值类型的四个成员各有其特定的用途:
- 整数(int):表示没有小数部分的数值,例如 -3、0、42。整数在 Python 中可以表示任意大小的数值,不受位数限制。
- 浮点数(float):表示带有小数部分的数值,例如 3.14、-0.5、2.0。浮点数在计算机中采用双精度二进制格式存储,因此部分十进制小数无法精确表示。
- 复数(complex):表示数学中的复数,形式为 a + bj,其中 a 为实部,b 为虚部。复数主要用于科学计算和工程领域。
- 布尔值(bool):仅包含 True 和 False 两个值,用于表示逻辑真与逻辑假。在 Python 的类型体系中,bool 是 int 的子类。同时,根据语言规范,True 在数值上等于 1,False 在数值上等于 0。
3 整数类型(int)
整数类型(Integer Type)用于表示没有小数部分的整数,例如 -5、0、100。与许多编程语言不同,Python 的整数具有任意精度特性,其大小仅受限于计算机可用内存,不会发生溢出。
3.1 整数的精度
Python 中的整数可以处理任意大小的数值,包括正整数、负整数和零。你可以直接进行大数运算,而无需担心数值范围超出固定位数(例如 32 位或 64 位)的限制。
# 02_integer_precision.py
# 演示整数的大数运算
num1 = 12345678901234567890
num2 = 86419753200000000000
result = num1 + num2
print(result)
程序运行结果如下所示:

💡 提示:任意精度
Python 的整数采用变长存储结构,能够根据数值大小动态调整所占用的内存字节数,因此可以处理天文数字级别的整数运算。这一点与 C 语言等需要区分短整数、长整数等类型的语言不同,Python 的整数类型统一涵盖所有整数范围。
3.2 整数分隔符
在书写较大整数时,数字位数较多,容易数错位数。Python 允许在数字字面量中使用下划线(_)作为视觉分隔符,以提高可读性。
在 Python 中,== 运算符用于比较两个对象的值是否相等。如果相等,比较结果为 True(表示 “真”);如果不相等,比较结果为 False(表示 “假”)。
# 03_integer_underscore.py
# 演示整数分隔符的使用
num1 = 1_000_000_000_000_000
num2 = 1000000000000000
print(num1)
print(num2)
print(num1 == num2) # 比较两者值是否相等
程序运行结果如下所示:

在 Python 解析代码时,下划线会被直接忽略,因此 1_000_000 与 1000000 在数值上完全等价。下划线可以放置在数字之间的任意位置,通常按千位分组(每三位一组)进行分隔,这是最符合阅读习惯的做法。此表示法同样适用于浮点数,但无论是整数还是浮点数,该用法均要求 Python 3.6 及以上版本。
3.3 整数对象的内存管理
Python 对整数对象的内存管理采用了一套优化策略,以降低频繁创建和销毁对象带来的性能开销。CPython 解释器引入了小整数池(Small Integer Cache)机制,对特定范围的整数进行复用。
小整数池
在 CPython 解释器中,闭区间 [-5, 256] 内的所有整数对象,在解释器启动时即被预先创建并缓存到内存中。当程序使用该范围内的整数时,所有变量都直接引用这些预先创建好的对象,而不会重新创建。
# 04_small_integer_pool.py
# 演示小整数池的复用机制
num1 = 10
num2 = 10
print(id(num1))
print(id(num2))
程序运行结果如下所示:

如输出所示,num1 和 num2 虽然各自赋值,但实际引用的是同一个内存对象,因此 id() 返回的地址完全相同。
⚠️ 注意:CPython 实现细节
闭区间 [-5, 256] 是 CPython 解释器的具体实现策略,并非 Python 语言规范中的强制性规定。其他 Python 解释器(如 PyPy、Jython)可能采用不同的范围或策略。本专栏内容均以 CPython(官方标准解释器)为准。
大整数的对象引用行为
对于超出闭区间 [-5, 256] 的整数,Python 默认不会预先创建并缓存对象。每次赋值时,通常会在内存中新建一个整数对象。
但在某些执行环境中(例如 PyCharm 的脚本运行模式),解释器会对整个代码块进行编译优化。当多个变量被赋值为相同的较大整数时,优化机制可能会复用同一个对象,以减少内存占用。
# 05_large_integer_behavior.py
# 演示大整数的对象引用行为
num3 = 1000
num4 = 1000
print(id(num3))
print(id(num4))
在 PyCharm 中通过 Ctrl + Shift + F10 执行此代码时,由于编译优化,两个 id() 输出的地址通常相同。
程序运行结果(PyCharm 中,通过 Ctrl + Shift + F10 执行)如下所示:

在交互式命令行(例如 CMD)中逐行执行时,由于每行独立编译,两个 id() 输出的地址通常不同。
程序运行结果(交互式命令行逐行执行)如下所示:

这一差异并非错误,而是不同执行环境对代码的优化策略不同所致。对于初学者而言,只需掌握以下核心要点:
- 小整数池范围固定为闭区间 [-5, 256],该范围内的整数在所有环境中均被复用。
- 超出该范围的整数,其对象复用行为不具有确定性,不应依赖此特性编写程序逻辑。
4 浮点数类型(float)
浮点数类型(Floating-Point Type)用于表示带有小数部分的数值,例如 3.14、-0.5、2.0。在 Python 中,所有带小数点的数字字面量均被视作浮点数。
4.1 浮点数的表示
浮点数的书写方式
浮点数可以使用两种方式书写:小数形式与科学计数法形式。
- 小数形式:直接写出整数部分与小数部分,例如 3.14、-0.5、2.0。小数部分即使为零(如 2.0),其类型仍为 float。小数形式也支持下划线作为视觉分隔符,例如 1_000_000.5(要求 Python 3.6 及以上版本)。
- 科学计数法形式:使用字母 e 或 E 表示 “乘以 10 的多少次方”。其基本格式为:系数e指数,表示系数 × 10^指数。
科学计数法的具体规则如下:
- 系数部分:可以是整数或浮点数,例如 1.3e7、13e6、0.5e-3 均为合法写法。
- 指数部分:必须是整数,且指数可以带正负号。正号通常省略,负号表示乘以 10 的负指数次幂(即除以 10 的相应次方)。指数不能是浮点数,例如 1.3e7.5 会引发语法错误。
- 大小写:字母 e 或 E 均可,两者完全等价。
- 下划线分隔:科学计数法同样支持下划线作为视觉分隔符,例如 1.23e1_000 在解析时等同于 1.23e1000(下划线被忽略)。下划线只能出现在系数部分或指数部分的内部,不能紧邻字母 e(或 E)。例如,1_e3(紧邻 e 左侧)与 1e_3(紧邻 e 右侧)均为非法写法。此特性在 Python 3.6 及以上版本中支持。
- 结果类型:无论系数或指数为何种形式,科学计数法表示的数字字面量始终返回 float 类型。
浮点数的表示范围与特殊值
Python 的浮点数基于 IEEE 754 双精度标准,其最大可表示数值约为 1.8e308,最小正数约为 5e-324。
当浮点数运算结果超出最大可表示范围时,会产生 inf(正无穷大)或 -inf(负无穷大)。例如,1e1000 就是一个超出表示范围的正数,其结果为 inf;-1e1000 的结果为 -inf。inf 与自身相等,即 inf == inf 的结果为 True;-inf == -inf 的结果同样为 True。
此外,浮点数运算中还可能存在 nan(Not a Number,非数字),通常由未定义的数学运算产生。与 inf 不同,nan 是一个特殊值,它不与任何值相等 —— 包括与自身进行比较时也返回 False。也就是说,nan == nan 的结果为 False。
# 06_float_representations.py
# 演示浮点数的小数形式与科学计数法表示
num1 = 3.14 # 普通浮点数
num2 = 1_000_000.5 # 浮点数中使用下划线分隔
num3 = 1.3e7 # 1.3 × 10^7
num4 = 13e6 # 13 × 10^6
num5 = 0.5e-3 # 0.5 × 10^-3
num6 = 1.23e1_000 # 正数值溢出,产生 inf
num7 = -1.23e1_000 # 负数值溢出,产生 -inf
print(num1)
print(num2)
print(num3)
print(num4)
print(num5)
print(num6)
print(num7)
程序运行结果如下所示:

4.2 浮点数精度问题
在计算机中,浮点数采用二进制格式存储。某些十进制小数无法用二进制精确表示,因此在运算时可能会产生微小的误差。
# 07_float_precision.py
# 演示浮点数运算的精度丢失
num1 = 0.1
num2 = 0.2
num3 = num1 + num2
print(num3)
程序运行结果如下所示:

需要说明的是,浮点数的精度丢失问题在大部分编程语言中都存在,其根源在于二进制与十进制之间的转换规则,属于计算机硬件层面的限制,并非 Python 语言本身的缺陷。
在实际编程中,这种微小误差在大多数场景下可以忽略。但对于需要精确计算的场景(例如金融计算),则需要使用 decimal 模块提供的十进制浮点数运算。
decimal 模块是 Python 标准库的一部分,其中的 Decimal 类型能够以十进制方式精确表示小数,避免了二进制浮点数带来的精度问题。
使用 Decimal 时,需要将数字以字符串形式传入,例如 Decimal("0.1")。这是因为如果直接传入浮点数(如 Decimal(0.1)),0.1 在传入之前已经被转换为二进制浮点数,精度丢失已经发生,Decimal 只能接收到一个不精确的近似值。只有使用字符串传入,Decimal 才能从源头上以十进制方式精确解析该数值。
以下代码演示了 Decimal 的用法:
# 08_decimal_precision.py
# 演示使用 Decimal 解决精度丢失
from decimal import Decimal
f1 = Decimal("0.1") # 以字符串形式传入,确保精度
f2 = Decimal("0.2")
f3 = f1 + f2
print(f3)
程序运行结果如下所示:

💡 提示:模块导入
from decimal import Decimal 的含义是从 decimal 模块中导入 Decimal 类型。关于模块导入的具体机制,后续专栏将专门讲解。目前你只需照此写法,将其视为一个固定用法即可。
💡 提示:浮点数的统一表示
Python 的 float 类型在底层采用双精度二进制格式存储(对应 C 语言的 double 类型),统一涵盖所有浮点数范围。与 C 语言等需要区分 float(单精度)和 double(双精度)等不同精度的语言相比,Python 使用单一的 float 类型即可满足所有浮点数使用需求。
5 布尔类型(bool)
布尔类型(Boolean Type)用于表示逻辑上的 “真” 与 “假”。在 Python 中,布尔类型只有两个值:True(真)和 False(假)。
5.1 布尔值的基本含义
True 和 False 是 Python 中的两个关键字,用于表示逻辑判断的结果。
# 09_bool_basic.py
# 演示布尔值的基本使用
bool1 = True
bool2 = False
print(bool1)
print(bool2)
程序运行结果如下所示:

5.2 布尔类型与整数类型的关系
根据 Python 的类型体系设计,bool 是 int 的子类。True 在数值上等于 1,False 在数值上等于 0。因此,布尔值可以直接参与数学运算。
# 10_bool_int_relation.py
# 演示布尔值与整数的关系
print(True + 3) # True 作为 1 参与加法
print(False + 3) # False 作为 0 参与加法
print(True == 1) # 比较 True 与 1 是否相等
print(False == 0) # 比较 False 与 0 是否相等
程序运行结果如下所示:

从输出可知,True 与 1 在数值上相等,False 与 0 在数值上相等。这一特性使得布尔值可以灵活地用于条件判断和数值计算中。
需要说明的是,虽然 True 在数值上等于 1,但它们并非同一个对象。True 和 False 是 Python 解释器启动时预先创建好的两个固定对象,程序中所有对 True 和 False 的引用都指向这两个固定对象。这一行为由 Python 语言规范保证,在所有 Python 解释器中均保持一致。
5.3 假值全集
在 Python 中,除了 False 本身之外,还有若干值在逻辑判断时会被视为 “假”。这些值统称为假值(Falsy)。在条件判断的上下文中,以下值均等价于 False:
- None
- False
- 数值零:0、0.0、0j(复数零)
- 空字符串:""
- 空容器:空列表 []、空元组 ()、空字典 {}、空集合 set()
除了上述假值之外,其他所有值在逻辑判断中均被视为 “真”。
📚 扩展:真值测试
在逻辑判断的上下文中,上述假值会表现出与 False 相同的效果,其余值则表现出与 True 相同的效果。后续学习条件判断语句时,会频繁使用这一规则。
5.4 值比较与身份比较
Python 提供了两种不同的比较方式:
- 值比较:使用 == 运算符,判断两个对象的值是否相等。
- 身份比较:使用 is 运算符,判断两个对象是否为同一个对象(即是否占用相同的内存地址)。
使用 is 运算符时,A is B 用于判断 A 与 B 是否指向内存中的同一个对象。is 的比较结果不受参数顺序影响 —— A is B 与 B is A 完全等价,两者均表达同一判断。
对于布尔值,== 比较的是逻辑值是否相等,而 is 比较的是对象身份是否相同。
# 11_bool_comparison.py
# 演示 == 与 is 的区别
bool1 = True
num1 = 1
bool2 = False
num2 = 0
print(bool1 == num1) # True 与 1 的值相等
print(bool1 is num1) # True 与 1 是否为同一个对象
print(bool2 == num2) # False 与 0 的值相等
print(bool2 is num2) # False 与 0 是否为同一个对象
程序运行结果如下所示:

输出结果表明:True 与 1、False 与 0 在值上均相等(== 返回 True),但它们并非同一个对象(is 返回 False)。这是因为 1 和 0 位于小整数池中,而 True 和 False 是 Python 解释器启动时预先创建好的两个固定对象。True 虽然数值上等于 1,但它与 1 在内存中是两个不同的对象;False 与 0 同理。
6 类型判断函数
在编写 Python 程序时,有时需要明确知道某个变量所引用对象的类型,或者判断一个对象是否属于某个特定类型。Python 提供了两个内置函数用于类型相关的判断:type() 和 isinstance()。
6.1 type() 查看类型
type() 函数用于获取对象的类型。调用时传入一个对象作为参数,该函数将返回对象的类型信息。
# 12_type_basic.py
# 演示 type() 的基本用法
num1 = 10
float1 = 3.14
bool1 = True
str1 = "hello"
print(type(num1))
print(type(float1))
print(type(bool1))
print(type(str1))
程序运行结果如下所示:

type() 返回的结果是一种类型对象(例如 <class 'int'> 表示整数类型),通常用于调试阶段确认变量的类型是否符合预期。
6.2 isinstance() 判断类型
isinstance() 函数用于判断一个对象是否属于某个特定类型。该函数需要传入两个参数:
- 第一个参数:待判断的对象。
- 第二个参数:目标类型。
函数返回 True 表示对象属于该类型,返回 False 表示不属于。
# 13_isinstance_basic.py
# 演示 isinstance() 的基本用法
num1 = 10
bool1 = True
print(isinstance(num1, int)) # 判断 num1 是否为 int 类型
print(isinstance(bool1, bool)) # 判断 bool1 是否为 bool 类型
print(isinstance(bool1, int)) # 判断 bool1 是否为 int 类型
程序运行结果如下所示:

从输出中可以看到:bool1 既是 bool 类型,也是 int 类型。bool 是 int 的子类,因此布尔值属于整数类型的范畴。
6.3 type() 与 isinstance() 的区别
type() 与 isinstance() 的核心区别在于对继承关系的处理方式不同:
- type() :直接返回对象所属的类型。如果对象属于某个子类,type() 返回的是子类本身,而不会将其视为父类类型。
- isinstance() :在判断时会考虑继承关系。如果对象属于某类型,或者属于该类型的子类,isinstance() 均返回 True。
下面的示例可以清晰地展示这一区别:
# 14_type_vs_isinstance.py
# 演示 type() 与 isinstance() 在处理继承关系时的区别
bool1 = True
print(type(bool1) == bool) # type 返回 bool,与 bool 比较结果为 True
print(type(bool1) == int) # type 返回 bool,与 int 比较结果为 False
print(isinstance(bool1, bool)) # bool1 属于 bool 类型,返回 True
print(isinstance(bool1, int)) # bool1 属于 int 的子类,返回 True
程序运行结果如下所示:

核心结论概括如下:
- type(bool1) == int 返回 False,因为 bool1 的类型是 bool,而 int 是 bool 的父类,type() 不识别继承关系。
- isinstance(bool1, int) 返回 True,因为 isinstance() 识别继承关系,认为布尔值属于整数类型。
7 字符串类型(str)
字符串类型(String Type)用于表示文本信息。在 Python 中,字符串是由零个或多个字符组成的字符序列,可以使用单引号或双引号括起来。
7.1 字符串的定义
字符串字面量可以使用单引号(')或双引号(")括起来。这两种方式在功能上完全等价,你可以根据个人偏好或内容的便利性进行选择。PEP 8(Python 官方编码规范)对此未做出强制性规定,唯一的建议是 “选定一种规则并坚持下去”,以保持项目中代码风格的一致性。
字符串长度可以为 0,即不包含任何字符。这种长度为 0 的字符串称为空字符串(Empty String),可以直接用一对紧挨着的引号表示,例如 '' 或 ""。
# 15_string_definition.py
# 演示字符串的两种定义方式
str1 = 'hello'
str2 = "world"
empty_str1 = '' # 空字符串,长度为 0
empty_str2 = "" # 空字符串,长度为 0
print(str1)
print(str2)
print(empty_str1) # 不输出任何内容
print(empty_str2) # 不输出任何内容
程序运行结果如下所示:

7.2 多行字符串
如果字符串需要跨越多行,可以使用三个单引号(''')或三个双引号(""")括起来。根据 PEP 8 的官方风格建议,推荐使用三个双引号。
三引号字符串会保留其中的换行符和缩进空格。
# 16_string_multiline.py
# 演示三引号定义多行字符串
str1 = """hello
world"""
print(str1)
str2 = """hello world
HELLO WORLD"""
print(str2)
程序运行结果如下所示:

在编写代码时,有时会遇到一行字符串过长的情况。为了保持代码的可读性,可以将字符串拆分为多行书写。但如果直接换行,Python 会报错,因为字符串字面量在行末未闭合。此时可以使用圆括号将多个字符串字面量括起来,Python 会自动将它们拼接为一个字符串,输出中不会产生额外的换行。
# 17_string_concat.py
# 演示使用圆括号将长字符串拆分为多行书写
# 错误写法:直接换行,字符串字面量在行末未闭合
# str_error = "Python 是一种广泛使用的高级编程语言,
# 其设计哲学强调代码的可读性和简洁的语法。"
# 正确写法:使用圆括号将多个字符串字面量括起来
str1 = (
"Python 是一种广泛使用的高级编程语言,"
"其设计哲学强调代码的可读性和简洁的语法。"
"Python 支持多种编程范式,包括面向对象、"
"命令式、函数式编程和过程式编程。"
)
print(str1)
程序运行结果如下所示:

输出中显示的全部内容都在同一行,未产生任何额外的空行。通过圆括号,可以将一个长字符串拆分为多行书写,既保持了代码的可读性,又确保了输出格式的整洁。
💡 提示:PyCharm 自动换行
在 PyCharm 中编写长字符串时,在需要换行的位置按下回车键,PyCharm 会自动添加引号和括号,将长字符串拆分为符合 Python 语法的多行字符串字面量拼接形式。
7.3 转义字符
在字符串中,某些特殊字符无法直接输入(例如换行、制表),或者直接输入会引起歧义(例如引号)。此时需要使用转义字符,即在普通字符前添加反斜杠(\),以改变其含义。
Python 中常用的转义字符如下:
| 转义字符 | 含义 |
|---|---|
| \\ | 反斜杠本身 |
| \' | 单引号 |
| \" | 双引号 |
| \n | 换行:将光标移至下一行行首 |
| \t | 水平制表符:将光标移至下一个制表位 |
| \r | 回车:将光标移至当前行行首 |
| \b | 退格:将光标左移一格 |
| \(行尾) | 续行:本行与下一行在逻辑上属于同一行 |
以下示例逐一演示上述转义字符的效果:
# 18_escape_sequences.py
# 演示各种转义字符的效果
print("反斜杠:\\")
print("单引号:\'")
print("双引号:\"")
print("换行:\n第一行\n第二行")
print("制表符:\t前面有Tab")
print("回车符:\r回车") # \r 将光标移到行首
print("退格符:abc\bd") # \b 退一格,删除前一个字符
print("续行符:"\
"此行与前一行拼接")
程序运行结果(PyCharm 中,通过 Ctrl + Shift + F10 执行)如下所示:

程序运行结果(交互式命令行逐行执行)如下所示:

⚠️ 注意:环境差异
上述表格中描述的是 \t、\r、\b 等转义字符在 ASCII 编码中的标准定义,即它们各自代表何种控制操作。但在实际输出时,其呈现效果取决于终端或控制台程序的具体实现。例如,制表符 \t 在不同环境中可能占据不同宽度;对于 \r 与 \b,在光标移动后,终端对新输出内容的覆盖或删除等处理方式也可能存在差异。
7.4 字符串中的引号处理
当字符串内容本身包含单引号或双引号时,有两种处理方式。
方式一:使用转义字符
在引号前添加反斜杠(\),将引号转义为普通字符。如果字符串内容包含的引号与外侧引号相同,则必须使用转义字符,否则会引发语法错误。
例如,'This is a 'string' too' 中的第二个单引号与外侧包裹的单引号相同,Python 会认为字符串在第二个单引号处已经结束,无法正确解析剩余内容。此时需要在内容中的单引号前添加反斜杠,即 \',将其转义为普通字符,Python 才能正确识别整个字符串。
以下代码展示了错误写法与正确写法:
# 19_escape_quotes.py
# 演示字符串中引号的处理:错误与正确
# 错误写法:内容中的单引号与外侧单引号冲突,Python 无法识别字符串的结束位置
# str_error = 'This is a 'string' too' # 取消注释会引发 SyntaxError
# 正确写法:使用转义字符 \' 将内容中的单引号转为普通字符
str_correct = 'This is a \'string\' too'
print(str_correct)
# 使用双引号包裹的字符串,若内容包含双引号,同样需要转义
str1 = "This is a \"string\""
print(str1)
程序运行结果如下所示:

方式二:选择另一种引号包裹
这是一种可选的替代做法。当字符串内容包含双引号时,可以使用单引号包裹整个字符串;当字符串内容包含单引号时,可以使用双引号包裹整个字符串。这种做法无需使用转义字符,代码更简洁易读。
# 20_quote_alternatives.py
# 演示使用不同引号包裹字符串以简化写法
str1 = 'This is a "string"'
str2 = "This is a 'string' too"
print(str1)
print(str2)
程序运行结果如下所示:

以上两种方式均可正确运行,你可以根据实际场景选择更清晰的方式。
如果字符串内容同时包含单引号和双引号,则只能使用转义字符来处理其中一种引号。
# 21_mixed_quotes.py
# 演示字符串中同时包含单引号和双引号时的处理方式
# 错误写法:无论使用单引号还是双引号包裹,都会与内容中的引号冲突
# str_error1 = 'She said, "It's a beautiful day"' # 内容中的单引号与外侧单引号冲突
# str_error2 = "She said, "It's a beautiful day"" # 内容中的双引号与外侧双引号冲突
# 正确写法:使用转义字符分别处理内容中的单引号和双引号
str_correct = "She said, \"It's a beautiful day\""
print(str_correct)
程序运行结果如下所示:

当字符串内容同时包含单引号和双引号时,无法通过切换外侧引号来完全避免冲突 —— 因为无论选择单引号还是双引号包裹,都会与内容中的某一种引号冲突。此时只能使用转义字符,对内容中的引号逐一进行转义处理。
8 字符串的内存优化机制
Python 对字符串对象的内存管理采用了一套优化策略,以降低内存占用并提升程序运行效率。其中最重要的两种机制是字符串驻留与单字符缓冲池。
8.1 字符编码与 ASCII 简介
计算机内部以二进制形式存储数据,每个字符(例如字母、数字、标点符号)都需要对应一个特定的二进制编码,才能在计算机中表示。这套映射规则称为字符编码(Character Encoding)。
在早期计算机发展中,最基础的字符编码是 ASCII(American Standard Code for Information Interchange,美国信息交换标准代码)。ASCII 编码使用 7 位二进制数表示一个字符,总共可以表示 128 个字符,其中包括:
- 英文字母(大写 A 到 Z,小写 a 到 z)
- 数字(0 到 9)
- 常用标点符号(如 !、@、#、$ 等)
- 部分控制字符(如换行符 \n、回车符 \r、制表符 \t 等)
ASCII 编码分为两类字符:控制字符(编码范围为 0 到 31)和可打印字符(编码范围为 32 到 127)。控制字符通常不可见,用于控制外围设备(如打印机、终端等);可打印字符则包括大小写英文字母、数字和常用标点符号,可以直接显示在屏幕上。
完整的 ASCII 编码对照表,可参考在线资源:https://www.lddgo.net/string/ascii。
ASCII 编码无法表示中文、日文、韩文等非英语字符,因为这些字符的数量远远超过了 ASCII 编码所能提供的 128 个位置。Python 3 内部统一使用 Unicode(统一码)字符集,它可以表示全球几乎所有书面语言中的字符,因此中文、日文等字符在 Python 字符串中可以直接使用。
完整的 Unicode 字符编码,可参考在线资源:https://www.lddgo.net/string/unicode-chart。
💡 提示:Unicode 与 Python
Python 3 中的字符串默认采用 Unicode 编码,因此你可以直接在字符串中写入中文、日文等非 ASCII 字符,无需额外设置。
8.2 字符串驻留机制
在 Python 中,某些字符串在创建时会被缓存到一个全局的字符串池中。当再次创建内容相同的字符串时,解释器会直接复用池中已有的对象,而非重新创建。这一机制称为字符串驻留(String Interning)。
字符串驻留并非对所有字符串生效。在 CPython 的实现中,仅包含 ASCII 字母、数字和下划线(即 Python 标识符中合法的字符)的字符串会被自动驻留。例如,"helloworld" 只包含字母,因此会被驻留;而 "hello world" 包含空格(空格不是 Python 标识符的合法字符),则不会被自动驻留。
# 22_string_intern_basic.py
# 演示字符串驻留的基本行为
str1 = "helloworld"
str2 = "helloworld"
print(id(str1))
print(id(str2))
print(str1 is str2)
程序运行结果(PyCharm 中,通过 Ctrl + Shift + F10 执行)如下所示:

程序运行结果(交互式命令行逐行执行)如下所示:

上述示例中,"helloworld" 仅包含字母,因此触发驻留机制。变量 str1 与 str2 引用的是同一个字符串对象。
对于包含非标识符字符的字符串,驻留行为则呈现不确定性。
# 23_string_intern_space.py
# 演示含非标识符字符的字符串的驻留行为
str1 = "hello world"
str2 = "hello world"
print(id(str1))
print(id(str2))
print(str1 is str2)
程序运行结果(PyCharm 中,通过 Ctrl + Shift + F10 执行)如下所示:

程序运行结果(交互式命令行逐行执行)如下所示:

该示例在 PyCharm 中运行时,由于解释器对整个脚本进行编译优化,含有空格的字符串仍然可能被复用。而在交互式命令行中逐行执行时,每一行独立编译,含有空格的字符串不会被驻留,因此会创建两个独立的对象。
⚠️ 注意:驻留行为取决于具体实现
字符串驻留机制属于 CPython 解释器的实现细节,并非 Python 语言规范中的强制性要求。不同版本的 CPython 或不同的 Python 解释器(如 PyPy、Jython)在驻留行为上可能存在差异。
8.3 单字符缓冲池
CPython 解释器对长度为 1 的字符进行了缓存优化。所有 ASCII 扩展字符范围内的单字符字符串(即 Unicode 编码 U+0000 到 U+00FF 的 256 个字符)在解释器启动时被预先创建并缓存。当程序中使用这些字符时,直接复用缓存中的对象。
ASCII 扩展字符范围(也称为 Latin-1)包含两部分:标准 ASCII 字符(0–127,包括英文字母、数字、常用标点符号以及空格)和扩展 ASCII 字符(128–255,包括西欧语言中使用的带附加符号的字母及特殊符号)。因此,所有 ASCII 字符以及部分西欧语言字符都在此缓冲池范围内。
非 ASCII 扩展范围的字符(例如中文、日文、韩文等)则不在缓冲池范围内,每次创建均为独立对象。但需要注意的是,在 PyCharm 等集成开发环境中,由于编译优化机制,某些非 ASCII 扩展范围的字符串也可能被复用,这一现象与单字符缓冲池无关,而是由环境优化策略导致的。
# 24_single_char_pool.py
# 演示单字符缓冲池的复用机制
char1 = "a"
char2 = "a"
char3 = " "
char4 = " "
char5 = "中"
char6 = "中"
print(id(char1))
print(id(char2))
print(id(char3))
print(id(char4))
print(id(char5))
print(id(char6))
print(char1 is char2)
print(char3 is char4)
print(char5 is char6)
程序运行结果(PyCharm 中,通过 Ctrl + Shift + F10 执行)如下所示:

程序运行结果(交互式命令行逐行执行)如下所示:

如输出所示:ASCII 范围内的字符在不同环境中均被复用;而中文等非 ASCII 扩展范围的字符在交互式命令行中不会被复用,每次创建均为独立对象。但在 PyCharm 等集成开发环境中,由于编译优化机制,这类字符也可能被复用。
更多推荐

所有评论(0)