4.字符串
1 字符串的本质
- 字符串的本质是:字符序列。
- Python 不支持单字符类型,单字符也是作为一个字符串使用的。
2 字符串的编码
Python 3 直接支持 Unicode,可以表示世界上任何书面语言的字符。
(图片未上传成功:Pasted-image-20241214195732)
用内置函数 ord() 可以把字符转换成对应的 Unicode 码;
用内置函数 chr() 可以把十进制数字转换成对应的字符。
print(ord('A'))
print(chr(65))
print(ord('小'))
(图片未上传成功:Snipaste_2022-06-17_22-54-18)
3 字符串的定义
字符串是 Python 中最常用的数据类型。通常用引号创建字符串,只要为变量分配一个值即可。
案例 1:使用单引号或双引号定义字符串变量。
str1 = 'abcdefg'
str2 = "hello world"
print(type(str1)) # <class 'str'>
print(type(str2)) # <class 'str'>
(图片未上传成功:Snipaste_2022-06-20_11-03-31)
案例 2:使用 3 个引号定义字符串变量。
name1 = '''I am Tom, Nice to meet you!'''
print(name1)
print(type(name1))
print('-' * 20)
name2 = """I am Jennify,
Nice to meet you!"""
print(name2)
print(type(name2))
(图片未上传成功:Snipaste_2022-06-20_11-04-58)
案例 3:思考如何使用字符串定义 I'm Tom。
使用单引号的情况:
str1 = 'I'm Tom'
运行结果:
(图片未上传成功:image-20210310101123953)
出现以上问题,是因为这段代码触发了语法错误(SyntaxError)。原因在于:单引号在字符串定义中必须成对出现,而 Python 解析器解析时,会自动把第一个单引号与最近的一个单引号视为一对。
如果一定要在单引号内部再放一个单引号,必须用 反斜杠(\) 转义。
str1 = 'I\'m Tom'
(图片未上传成功:Snipaste_2022-06-20_11-22-03)
使用双引号的情况:
str2 = "I'm Tom"
(图片未上传成功:Snipaste_2022-06-20_11-22-10)
3.1 空字符串和 len() 函数
len() 用于计算字符串含有多少个字符。
s = '小龙女'
print(len(s))
(图片未上传成功:Snipaste_2022-06-26_22-55-14)
Python 允许空字符串存在,它不包含任何字符、长度为 0。例如:
s = ''
print(len(s))
(图片未上传成功:Snipaste_2022-06-26_22-55-40)
3.2 转义字符
可以用 \ + 特殊字符 实现某些难以用普通字符表示的效果,例如换行。常见转义字符如下:
| 转义字符 | 描述 |
|---|---|
\(在行尾时) |
续行符 |
\\ |
反斜杠符号 |
\' |
单引号 |
\" |
双引号 |
\b |
退格(Backspace) |
\n |
换行 |
\t |
横向制表符 |
\r |
回车 |
4 字符串输入
在 Python 代码中,可以用 input() 方法接收用户的输入。记住:在 Python 中,input() 方法返回的结果是 字符串类型 的数据。
name = input('请输入您的姓名:')
age = input('请输入您的年龄:')
address = input('请输入您的住址:')
print(name, age, address)
5 字符串的输出
5.1 普通输出
# 伪代码:演示两种调用形态
print(变量名称)
print(变量名称1, 变量名称2, 变量名称3)
5.2 格式化输出
① 百分号(Python 2 与 Python 3 均支持)
name = input('请输入您的姓名:')
age = input('请输入您的年龄:')
address = input('请输入您的住址:')
print('我的名字是%s,今年%d岁了,家里住在%s...' % (name, age, address))
② format 方法(Python 3)
print('我的名字是{},今年{}岁了,家里住在{}...'.format(name, age, address))
③ f-string 形式(Python 3.6+)
print(f'我的名字是{name},今年{age}岁了,家里住在{address}...')
延伸:
name = input('请输入您购买商品的名称:')
price = float(input('请输入您购买商品的价格:')) # 18.5
print(f'购买商品名称:{name},商品价格:{price:f}')
6 字符串拼接
-
可以用
+把多个字符串拼接起来,例如'aa' + 'bb' ==> 'aabb'。-
+两边都是字符串时,执行拼接。'aa' + 'bb'(图片未上传成功:Snipaste_2022-06-26_23-05-51)
-
+两边都是数字时,执行加法运算。1 + 1(图片未上传成功:Snipaste_2022-06-26_23-09-19)
-
+两边类型不同时,抛出异常。1 + 'a'(图片未上传成功:Snipaste_2022-06-26_23-09-42)
-
- 可以把多个字面字符串直接放在一起实现拼接,例如
'aa' 'bb' ==> 'aabb'。
'aa' 'bb'
(图片未上传成功:Snipaste_2022-06-26_23-10-53)
7 字符串复制
用 * 可以实现字符串复制。
'小龙女' * 2
(图片未上传成功:Snipaste_2022-06-26_23-12-26)
8 str() 实现数字转型字符串
str() 可以把其他数据类型转换为字符串。
print(str(5.20))
print(str(314e-2))
print(str(True))
(图片未上传成功:Snipaste_2022-06-26_23-18-11)
调用 print() 时,解释器会自动调用 __str__(),把非字符串对象转成字符串。这部分内容在面向对象章节详细讲解。
9 字符串在计算机底层的存储形式
在计算机中,Python 的字符串属于序列结构,因此其底层存储占用一段连续的内存空间。
str1 = 'itheima'
结构原理图:
(图片未上传成功:image-20210310104348286)
10 聊聊索引下标
索引下标就是编号。以火车座位号类比:座位号的作用是按编号快速找到对应座位;同理,下标的作用就是通过下标快速找到对应的数据。
(图片未上传成功:image-20210310104901506)
举个例子:
name = 'abcdef'
print(name[0]) # a
print(name[3]) # d
(图片未上传成功:image-20210310104907464)
11 使用 [] 提取字符
字符串的本质就是字符序列,可以在字符串后面加 [],在 [] 中指定偏移量,从而提取该位置的单个字符。
-
正向搜索:
最左侧第一个字符偏移量为 0,第二个为 1,以此类推,直到
len(str)-1为止。 -
反向搜索:
最右侧第一个字符偏移量为 -1,倒数第二个为 -2,以此类推,直到
-len(str)为止。
a = 'abcdefghijklmnopqrstuvwxyz'
a[0]
a[3]
a[26-1]
a[-1]
a[-26]
a[-30]
(图片未上传成功:Snipaste_2022-06-27_14-37-23)
(图片未上传成功:Snipaste_2022-06-27_14-37-32)
12 字符串切片
12.1 什么是字符串切片
所谓切片,就是对操作对象截取其中一部分的操作。字符串、列表、元组都支持切片。
12.2 字符串切片基本语法
包左不包右:
# 伪代码:展示完整语法形态
序列名称[起始偏移量 start : 终止偏移量 end : 步长 step]
- 不包含结束位置下标对应的数据,正负整数均可;
- 步长是选取间隔,正负整数均可:正数从左向右,负数从右向左。默认步长为 1。
☆ 典型操作(三个量均为正数)如下:
| 操作和说明 | 示例 | 结果 |
|---|---|---|
[:] 提取整个字符串 |
"abcdef"[:] |
"abcdef" |
[start:] 从 start 索引开始到结尾 |
"abcdef"[2:] |
"cdef" |
[:end] 从头开始直到 end-1 |
"abcdef"[:2] |
"ab" |
[start: end] 从 start 到 end-1 |
"abcdef"[2:4] |
"cd" |
[start: end: step] 从 start 提取到 end-1,步长是 step |
"abcdef"[1:5:2] |
"bd" |
☆ 其他操作(三个量为负数)的情况:
| 操作和说明 | 示例 | 结果 |
|---|---|---|
| 倒数三个 | "abcdefghijklmnopqrstuvwxyz"[-3:] |
"xyz" |
| 倒数第八个到倒数第三个(包左不包右) | "abcdefghijklmnopqrstuvwxyz"[-8: -3] |
"stuvw" |
| 步长为负,从右到左反向提取 | "abcdefghijklmnopqrstuvwxyz"[:: -1] |
"zyxwvutsrqponmlkjihgfedcba" |
如果还是有些陌生,没关系,举个例子:
numstr = '0123456789'
如果想对 numstr 进行切片,如下图所示:
(图片未上传成功:image-20210310110051093)
number = '0123456789'
number[0: 6]
number[0: 6: 1]
number[0: 6: 2]
number[-1:-6:-1]
number[-1::-1]
(图片未上传成功:Snipaste_2022-07-08_22-36-35)
str1 = 'abcdefg'
len(str1)
str1[-100::]
str1[-69:-3:]
str1[-100:100]
(图片未上传成功:Snipaste_2022-06-27_14-59-30)
12.3 字符串切片小口诀
记口诀:切片其实很简单,只顾头来尾不管,步长为正正向移,步长为负则逆向移
如果还不太清楚这个原理,建议对字符串画图理解:
(图片未上传成功:image-20210310111904137)
12.4 字符串切片的小栗子
案例 1:
numstr = '0123456789'
# 1、从 2 到 5 开始切片,步长为 1
print('numstr[2:5:1]:', numstr[2:5:1])
print('numstr[2:5]:', numstr[2:5])
# 2、只有结尾的字符串切片:代表从索引为 0 开始,截取到结尾字符 -1 的位置
print('numstr[:5]:', numstr[:5])
# 3、只有开头的字符串切片:代表从起始位置开始,已知截取到字符串的结尾
print('numstr[1:]:', numstr[1:])
# 4、获取或拷贝整个字符串
print('numstr[:]:', numstr[:])
# 5、调整步阶:类似求偶数
print('numstr[::2]:', numstr[::2])
# 6、把步阶设置为负整数:类似字符串翻转
print('numstr[::-1]:', numstr[::-1])
# 7、起始位置与结束位置都是负数(遵循一个原则:必须是从左向右截取)
print('numstr[-4:-1]:', numstr[-4:-1])
# 8、结束字符为负数,如截取 012345678
print('numstr[:-1]:', numstr[:-1])
(图片未上传成功:Snipaste_2022-06-26_23-33-21)
案例 2:给定一个图片的名称为 avatar.png,使用 Python 方法获取这个图片的名称 avatar 以及这个图片的后缀 .png。
分析:
- 建议先获取点号的位置(此处尚未学到相关方法,只能一个一个数);
- 从开头切片到点号位置,得到文件名;
- 从点号开始切片到结尾,得到文件后缀。
filename = 'avatar.png'
# 获取点号的索引下标
index = 6
# 使用切片截取文件的文件
name = filename[:index]
print(f'上传文件的名称:{name}')
# 使用切片截取文件的后缀
postfix = filename[index:]
print(f'上传文件的后缀:{postfix}')
13 字符串的操作方法(内置)
13.1 字符串中的查找方法
所谓字符串查找方法,就是查找子串在字符串中的位置或出现的次数。
基本语法:
# 伪代码:展示调用形态
字符串.find(要查找的字符或者子串)
| 函数 | 作用 |
|---|---|
find() |
检测某个子串是否包含在这个字符串中,如果在返回这个子串开始的位置下标,否则则返回 -1。 |
index() |
检测某个子串是否包含在这个字符串中,如果在返回这个子串开始的位置下标,否则则报异常。 |
rfind() |
和 find() 功能相同,但查找方向为右侧开始。 |
rindex() |
和 index() 功能相同,但查找方向为右侧开始。 |
count() |
返回某个子串在字符串中出现的次数。 |
13.1.1 find() 方法
作用:检测某个子串是否包含在字符串中,若在则返回该子串开始的位置下标,否则返回 -1。
# 定义一个字符串
str1 = 'hello world hello linux hello python'
# 查找 linux 子串是否出现在字符串中
print(str1.find('linux'))
# 在 str1 中查找不存在的子串
print(str1.find('and'))
(图片未上传成功:Snipaste_2022-06-27_15-19-53)
案例:使用 input 方法输入任意一个文件名称,求点号的索引下标。
filename = input('请输入您要上传文件的名称:')
# 获取点号的索引下标
index = filename.find('.')
print(index)
# 求文件名称
print(filename[:index])
# 求文件后缀
print(filename[index:])
(图片未上传成功:Snipaste_2022-06-27_15-20-04)
13.1.2 index() 方法
index() 方法的功能与 find() 完全一致,唯一区别在于查找的子串不在字符串中时:find() 返回 -1,而 index() 直接报错。
str1 = 'apple, banana, orange'
# 判断apple是否出现在字符串str1中
print(str1.index('apple'))
print(str1.index('pineapple'))
运行结果:
(图片未上传成功:Snipaste_2022-06-27_15-22-46)
13.1.3 rfind() 与 rindex() 方法
r 即 right,代表从右侧开始查找。
# 伪代码:展示调用形态
字符串序列.rfind(子串)
字符串序列.rindex(子串)
强调:rfind() 与 rindex() 适合子串在字符串中出现多次的情况。
案例:文件名为 20210310axvu.avatar.png,其中点号出现两次。此时若要获取后缀 .png,代码该如何写?
filename = '20210310axvu.avatar.png'
# 求出点号在字符串中第一次出现的位置
# index = filename.find('.')
# print(index)
# 求出点号在字符串中最后一次出现的位置
index = filename.rfind('.')
print(index)
print(filename[:index])
print(filename[index:])
(图片未上传成功:Snipaste_2022-06-27_15-29-45)
rfind() 与 rindex() 在语法上完全一致,唯一区别是子串不存在时:rfind() 返回 -1,rindex() 报错。
13.1.4 count() 方法
主要功能:求子串在字符串中出现的次数。
基本语法:
# 伪代码:展示完整参数
字符串.count('子串', 开始位置下标, 结束位置下标)
案例:获取字符串中 and 关键字出现的次数。
str1 = 'hello world and hello linux and hello python'
# 不限定字符串长度
ands = str1.count('and')
# 限定开始查找的位置和结束位置
# ands = str1.count('and', 10, 30)
print(f'and字符串出现的次数为:{ands}')
(图片未上传成功:Snipaste_2022-06-27_15-31-15)
13.1.5 查找方法效率问题
-
find和rfind效率高吗?要不要用?这两个方法效率并不高——都是在字符串中遍历搜索。但若「找子串」的工作本身必不可少,那就只能这么做,能少做就少做。
index方法与find很像,缺点在于找不到时抛异常,因此推荐用find。count方法效率极低,不推荐使用。字符串查找问题往往涉及较复杂的算法,难度较大,偶尔用一下查找方法是可行的。
-
时间复杂度
find、index与count方法都是O(n)。随着字符串数据规模增大,效率会下降。
13.2 字符串的修改方法
所谓修改字符串,就是通过函数(方法)的形式修改字符串中的数据。
| 函数 | 作用 |
|---|---|
replace() |
返回替换后的字符串 |
split() |
返回切割后的列表序列 |
capitalize() |
首字母大写 |
title() |
所有单词首字母大写 |
upper() 与 lower() |
返回全部大写或小写的字符串 |
swapcase() |
产生新的字符串,新字符串所有字母大小写转换 |
lstrip()、rstrip() 与 strip() |
去除左边、右边以及两边的空白字符 |
ljust()、rjust() 与 center() |
返回原字符串左对齐、右对齐以及居中对齐 |
13.2.1 replace() 方法
基本语法:
# 伪代码:展示完整参数
字符串.replace(要替换的内容, 替换后的内容, [替换的次数])
案例:编写一个字符串,然后把字符串中的 linux 替换为 python。
str1 = 'hello linux and hello linux'
# 把字符串中所有 linux 字符替换为 python
print(str1.replace('linux', 'python'))
# 把字符串中的第一个 linux 进行替换为 python
print(str1.replace('linux', 'python', 1))
# 把 and 字符串替换为 &&
print(str1.replace('and', '&&'))
(图片未上传成功:Snipaste_2022-06-27_16-16-45)
实际工作中,replace 主要用于实现关键字替换或过滤功能,例如 北京 ==> BJ、论坛关键字过滤、人民币 ==> 软妹币。
13.2.2 split() 方法
作用:对字符串进行切割操作,返回一个 list() 列表类型的数据。
str1 = 'apple-banana-orange'
print(str1.split('-'))
(图片未上传成功:Snipaste_2022-06-27_16-18-12)
13.2.3 capitalize() 方法
作用:把字符串的首字母大写,其他字符全部小写。
str1 = 'i love Linux and Java'
print(str1.capitalize())
(图片未上传成功:Snipaste_2022-06-27_16-20-18)
13.2.4 title() 方法
作用:把字符串中的所有单词的首字母大写,组成大驼峰。
str1 = 'myName'
# 把str1变成首字母大写字符串
print(str1.capitalize())
str2 = 'student_manager'
# 把str2变成大驼峰
print(str2.title().replace('_', ''))
(图片未上传成功:Snipaste_2022-06-27_16-22-05)
13.2.5 upper() 与 lower() 方法
upper():把字符串全部转换为大写形式。
lower():把字符串全部转换为小写形式。
# 用户名以及密码验证案例
username = input('请输入您的账号:')
password = input('请输入您的密码:')
# 把 username 和 password 全部转换为大写或小写
print(username.lower())
print(password.upper())
(图片未上传成功:Snipaste_2022-06-27_16-27-51)
(图片未上传成功:Snipaste_2022-06-27_16-25-38)
13.2.6 swapcase() 方法
作用:产生一个新字符串,其中所有字母的大小写都被转换。
str1 = 'I love Python and Linux'
print('转换前:', str1)
print('转换后:', str1.swapcase())
(图片未上传成功:Snipaste_2022-06-27_19-27-01)
13.2.7 lstrip()、rstrip() 与 strip()
strip() 的主要作用:删除字符串两边的空白字符(如空格)。
lstrip() 即 left + strip,作用:只删除字符串左边的空白字符。
rstrip() 作用:只删除字符串右边的空白字符。
# 用户名验证案例
username = input('请输入您的账号:')
# 去除username两边的空白字符
print(len(username))
print(username.strip())
print(len(username.strip()))
(图片未上传成功:Snipaste_2022-06-27_16-31-52)
13.2.8 ljust()、rjust()、center()
作用:返回原字符串左对齐、右对齐以及居中对齐。
基本语法:
# 伪代码:展示完整参数
字符串序列.ljust(长度, [填充字符])
未指定填充字符时,默认用空格填充。
案例:定义一个字符串,要求返回长度为 10 个字符,不足部分用 . 填充。
str1 = 'python'
# 左对齐
print(str1.ljust(10, '.'))
# 右对齐
print(str1.rjust(10, '#'))
# 居中对齐
print(str1.center(10))
print(str1.center(10, '@'))
(图片未上传成功:Snipaste_2022-06-27_16-35-49)
13.2.9 split() 分割和 join() 合并
split() 可以基于指定分隔符把字符串分隔成多个子字符串(存入列表)。若不指定分隔符,则默认使用空白字符(换行符 / 空格 / 制表符)。
a = "to be or not to be"
print(a.split()) # ['to', 'be', 'or', 'not', 'to', 'be']
print(a.split('be')) # ['to ', ' or not to ', '']
(图片未上传成功:Snipaste_2022-06-27_17-03-04)
join() 的作用与 split() 刚好相反:把一系列子字符串连接起来。
a = ['sxt', 'sxt100', 'sxt200']
print('*'.join(a))
(图片未上传成功:Snipaste_2022-06-27_17-08-03)
测试 + 与 join() 的效率差异:
import time
a = ['sxt', 'sxt100', 'sxt200']
'*'.join(a)
time01 = time.time() # 起始时刻
a = ""
for i in range(1000000):
a += "sxt"
time02 = time.time() # 终止时刻
print("运算时间:" + str(time02 - time01))
time03 = time.time() # 起始时刻
li = []
for i in range(1000000):
li.append("sxt")
a = "".join(li)
time04 = time.time() # 终止时刻
print("运算时间:" + str(time04 - time03))
(图片未上传成功:Snipaste_2022-06-27_17-13-55)
13.3 字符串的判断方法
所谓判断,就是判断真假,返回结果是布尔型数据:True 或 False。
| 函数 | 作用 |
|---|---|
startswith() |
检查字符串是否以指定子串开头,是则返回 True,否则返回 False。若设置开始和结束位置下标,则在指定范围内检查。 |
endswith() |
检查字符串是否以指定子串结尾,是则返回 True,否则返回 False。若设置开始和结束位置下标,则在指定范围内检查。 |
isalpha() |
若字符串所有字符(至少有一个)都是字母,返回 True,否则返回 False。 |
isdigit() |
若字符串只包含数字,返回 True,否则返回 False。 |
isalnum() |
Python isalnum() 方法检测字符串是否由字母和数字组成:若所有字符(至少有一个)都是字母或数字,返回 True,否则返回 False。 |
isspace() |
若字符串中只包含空白,返回 True,否则返回 False。 |
13.3.1 startswith()
# 伪代码:展示完整参数
字符串.startswith('子串', [开始位置下标, [结束位置下标]])
作用:检查字符串是否以指定子串开头,是则返回 True,否则返回 False。若设置开始和结束位置下标,则在指定范围内检查。
str1 = 'python program'
str2 = 'I love Python and Linux'
print(str1.startswith('python'))
print(str2.startswith('Python', 7, -1))
(图片未上传成功:Snipaste_2022-06-27_16-41-30)
13.3.2 endswith()
# 伪代码:展示完整参数
字符串.endswith('子串', [开始位置下标, [结束位置下标]])
作用:检查字符串是否以指定子串结尾,是则返回 True,否则返回 False。若设置开始和结束位置下标,则在指定范围内检查。
str2 = 'avatar.png'
print(str2.endswith('.png'))
if str2.endswith('.png') or str2.endswith('.jpg') or str2.endswith('.gif'):
print('是一张图片格式的图片')
else:
print('您上传的文件格式异常')
(图片未上传成功:Snipaste_2022-06-27_16-43-02)
13.3.3 isalpha()
作用:若字符串只包含字母则返回 True,否则返回 False。
str1 = 'admin'
str2 = 'admin123'
print(str1.isalpha()) # True
print(str2.isalpha()) # False
(图片未上传成功:Snipaste_2022-06-27_16-48-06)
13.3.4 isdigit()
作用:若字符串只包含数字则返回 True,否则返回 False。
password = input('请输入您的银行卡密码:')
if len(password) == 6 and password.isdigit():
print('输入密码成功,正在验证...')
else:
print('密码输入错误,请重新输入')
(图片未上传成功:image-20220627164957513)
(图片未上传成功:Snipaste_2022-06-27_16-50-14)
13.3.5 isalnum()
作用:Python isalnum() 方法检测字符串是否只由字母和数字组成。若所有字符都是字母或数字,返回 True,否则返回 False。
username = input('请输入的您的用户名(只能为字母+数字形式):')
if username.isalnum():
print('合理的用户名,正在录入系统...')
else:
print('输入的用户名有误,请重新输入...')
(图片未上传成功:Snipaste_2022-06-27_16-51-25)
(图片未上传成功:Snipaste_2022-06-27_16-51-38)
13.3.6 isspace()
作用:若字符串中只包含空白,则返回 True,否则返回 False(逆向思维)。
str1 = ' ' # 最少要包含一个空白字符
print(str1.isspace())
username = input('请输入的您的用户名:')
if len(username) == 0 or username.isspace():
print('您没有输入任何字符...')
else:
print(f'您的输入的字符{username}')
(图片未上传成功:Snipaste_2022-06-27_16-53-38)
13.3.7 判断方法效率问题
startswith 与 endswith 本身效率较高、可以使用,因为通常要比对的子串规模较小。但二者的效率会随子串规模变大而降低,时间复杂度为 O(n)(n 为子串长度),因此子串规模较大时要慎用,例如 DNA 序列中的子串匹配问题。
14 字符串比较
可以直接用 == 比较两个字符串是否含有相同字符。
也可以用 is / is not 判断两个对象是否为同一个对象——比较的是对象地址,即 id(obj1) == id(obj2)。
15 成员操作符
in / not in 关键字,用于判断某个字符(子字符串)是否存在于字符串中。
16 字符串驻留机制
16.1 什么是字符串的驻留机制
字符串驻留:一种在内存中仅保存一份不可变字符串的方法(相同的字符串只保留一份)。
不同的值被存放在字符串的驻留池中,Python 的驻留机制对相同字符串只保留一份拷贝:后续创建相同字符串时不会开辟新空间,而是把已有字符串的地址赋给新变量。
a = 'hello'
b = "hello"
c = """hello"""
print(a, id(a))
print(a, id(b))
print(a, id(c))
(图片未上传成功:Snipaste_2022-06-27_17-54-05)
16.2 原理
- 系统维护一个
interned字典,记录已被驻留的字符串对象。 - 字符串对象需要驻留时,先在
interned中检测是否存在:若存在,引用数+1;不存在则记录到interned中。
16.3 驻留机制的优缺点
- 背景:Python 一切皆对象,频繁创建和销毁对象会影响性能。
- 优点:需要值相同的字符串时,可直接从字符串池中取用,避免频繁创建和销毁,提升效率、节约内存。
- 缺点:创建驻留字符串需要花费更多时间。
16.4 驻留机制的几种情况(交互模式)
这里强调「交互模式」,是因为 PyCharm 等平台对字符串做了优化,本来不符合条件的字符串也会指向同一位置。
- 字符串长度为 0 或 1;
- 符合标识符的字符串(只包含字母、数字、下划线);
- 字符串只在编译时驻留,而非运行时;
[-5,256]之间的整数数字。
案例
-
字符串长度为 0 时:
s1 = '' s2 = '' print(s1 is s2)
Jupyter:
(图片未上传成功:Snipaste_2022-06-27_18-13-27)
PyCharm:
(图片未上传成功:file-20260415224600413)
-
字符串长度为 1 时:
s1 = '@' s2 = '@' print(s1 is s2)
Jupyter:
(图片未上传成功:Snipaste_2022-06-27_18-13-34)
PyCharm:
(图片未上传成功:file-20260415225007735)
-
符合标识符的字符串(只包含字母、数字和下划线):
s1 = 'abc' s2 = 'abc' print(s1 is s2) s1 = 'abc@' s2 = 'abc@' print(s1 is s2)
Jupyter:
(图片未上传成功:Snipaste_2022-06-27_18-13-42)
PyCharm:IDE 中做了优化。
(图片未上传成功:file-20260415224811984)
-
字符串只在编译时进行驻留,而非运行时:
s1 = 'xyz' s2 = 'xy' + 'z' # 编译时 s3 = ''.join(['xy', 'z']) # 运行时 print(s1 is s2) print(s1 is s3)
Jupyter:
(图片未上传成功:Snipaste_2022-06-27_18-13-48)
PyCharm:
(图片未上传成功:file-20260415225222509)
-
[-5,256]之间的整数数字:s1 = -5 s2 = -5 print(s1 is s2) s1 = -6 s2 = -6 print(s1 is s2)
Jupyter:
(图片未上传成功:Snipaste_2022-06-27_18-13-54)
PyCharm:IDE 中所有整数都会被保存;[-5, 256] 仍底层用数组实现,不在 [-5, 256] 范围内的数则缓存到链表中,不连续分配空间。
(图片未上传成功:file-20260415225332411)
16.5 强制驻留 sys.intern
sys 模块中的 intern 方法可以强制两个字符串指向同一个对象。
import sys
s1 = 'abc%'
s2 = 'abc%'
print(s1 is s2)
s1 = sys.intern(s2)
print(s1 is s2)
(图片未上传成功:Snipaste_2022-06-27_19-15-12)
17 可变字符串
在 Python 中,字符串属于不可变对象,不支持原地修改;若修改其中的值,会自动创建新的字符串对象。但我们确实常有「原地修改」的需求,这时可以使用 io.StringIO 对象或 array 模块。
import io
s = "hello, sxt"
sio = io.StringIO(s)
print(sio)
print(sio.getvalue())
sio.seek(7)
sio.write("g")
print(sio.getvalue())
(图片未上传成功:Snipaste_2022-06-27_20-13-17)
18 术语表(重要英文单词)
18.1 编码与表示
| 术语 | 全拼 / 说明 | 释义 |
|---|---|---|
| Unicode | — | 统一码,为世界上所有字符分配唯一码位的标准 |
| UTF-8 | Unicode Transformation Format - 8-bit | 变长编码,英文 1 字节、常用中文 3 字节、增补字符 4 字节 |
| GBK | 汉字内码扩展规范 | 中文编码,英文 1 字节、中文 2 字节 |
| ASCII | American Standard Code for Information Interchange | 美国信息交换标准代码,Unicode 的子集 |
| code point | — | 码位,Unicode 中字符的唯一编号,如 'A' = U+0041 |
| PEP | Python Enhancement Proposal | Python 增强提案;PEP 393 定义了灵活字符串表示 |
| UCS | Universal Character Set | 通用字符集;UCS-2(2 字节)/ UCS-4(4 字节) |
sys.maxunicode |
— | 当前解释器支持的最大 Unicode 码位,Python 3 为 1114111(0x10FFFF) |
18.2 操作与方法
| 术语 | 全拼 / 说明 | 释义 |
|---|---|---|
| indexing | — | 索引,用 [] 按下标取单个元素;越界抛 IndexError |
| slicing | — | 切片,用 [start:end:step] 取子序列;越界不报错 |
| offset | — | 偏移量,即下标;支持负数(-1 表示最后一个) |
| stride / step | — | 步长,切片的第三个参数;负数表示反向 |
| escape character | — | 转义字符,以 \ 开头,如 \n、\t、\\ |
| raw string | — | 原始字符串,前缀 r,反斜杠不转义 |
| implicit concatenation | — | 隐式拼接,相邻字面量自动合并,如 'aa' 'bb' |
| immutable | — | 不可变;字符串创建后内容不可改,"修改"实为新建 |
| intern / interning | — | 驻留,相同字符串只存一份的机制 |
| constant folding | — | 常量折叠,编译器在编译期合并相同字面量 |
sys.intern() |
— | 强制驻留函数 |
io.StringIO |
— | 内存中的类文件字符串缓冲区,适合频繁追加 |
18.3 判断类方法
| 术语 | 全拼 / 说明 | 释义 |
|---|---|---|
isdecimal() |
— | 是否只含十进制数字(最严格,推荐用于数字校验) |
isdigit() |
— | 是否只含数字(含上标 '²',不含分数 '½') |
isnumeric() |
— | 是否只含数值字符(最宽,含 '½'、'²'、中文数字) |
isalpha() |
— | 是否只含字母(含 Unicode 字母,不只 a-z) |
isalnum() |
— | 是否只含字母和数字 |
isspace() |
— | 是否只含空白字符 |
isascii() |
— | 是否全部为 ASCII 字符(Python 3.7+) |
19 系列导航
20 参考链接
- Python 3 官方文档(中文)
- 字符串方法 — 官方文档
- 文本序列类型 str — 官方文档
- Unicode HOWTO — 官方文档
- PEP 393 — 灵活字符串表示
- 格式规范迷你语言
io.StringIO— 官方文档os.path.splitext()— 官方文档
分类:Python

浙公网安备 33010602011771号