Python 教程

第 3 章:字符串的秘密

本章内容限 Python 3.12+ 用户

0. 什么是字符串?

本节目标:初步了解字符串
预计用时:5~10 分钟
完成标志:能初步理解字符串的作用及意义

0.1 本节正文

字符串(String),顾名思义,就是一串字符。本质上是一个 由 Unicode 字符组成的、不可变的有序序列。

它是最常用的文本表示方式——你写的名字、地址、聊天内容,在程序里都是用字符串来存放的。

“由 Unicode 字符组成的、不可变的有序序列”,这句话有点绕,我们拆开来理解。

关键词人话
Unicode 字符几乎所有文字都能装——中文、英文、日文、Emoji,全包
不可变一旦创建,内容就不能原地修改。你看着像是在改,实际上是创建了一个新字符串
有序序列每个字符都有位置编号,可以按位置取出来,顺序固定

Unicode 你不陌生。前面章节提到编码时说过:Python 内部用 Unicode 表示所有字符。这就意味着你可以直接在字符串里写中文,不用做任何特殊声明。

比如这样:

name = "张三"
greeting = "Hello,世界"
emoji = "🎉"

字符串的“不可变”听起来抽象,但你早就见过它。第2章 2.2 小节里提过:不可变对象不能原地修改,任何“修改”操作都会创建新对象。字符串就是不可变对象之一。

s = "Hello"
s = s + "World!"    # 看似是修改,实则是原地创建了个新的字符串

至于“有序序列”——意思是字符串里的每个字符都有编号(索引),从 0 开始。你能通过位置取到特定字符,后面会单独讲。

现在你只需要记住一句话:字符串就是一段能直接写中文的文本。至于“不可变”和“有序序列”的含义,会在后续用到时自然展开。先有个印象就行。

0.2 本节小结

  • 字符串就是文本:名字、地址、聊天内容,凡是跟文字打交道的地方就有字符串。
  • Unicode 是它的底气:你可以直接写中文、英文、日文、Emoji,不用做任何特殊处理。
  • 不可变、有顺序:这两个特性你现在不需要背。知道它们存在就行,后面用到时会自然熟悉。

这一节你只需要记住一句话:字符串就是一段能直接写中文的文本。 下一节,我们开始实际操作——怎么造一个字符串、怎么把它们拼在一起、怎么知道它有多长。

1. 字符串的创建与拼接

本节目标:学会创建字符串,并掌握几种拼接字符串的方法
预计用时:15~20 分钟
完成标志:能根据不同场景选择合适的引号创建字符串,并能用 + 拼接字符串

1.1 创建字符串:三种引号怎么选?

创建字符串最简单的方式,就是把文本放在引号里。

Python 提供了三种写法,它们的用途各不相同:

name = "张三"           # 双引号
father = '李四'         # 单引号
text = """I'm LanWan
I like coding
code make me happy
Python is world most great language one of.
"""                     # 三引号(保留换行)

那么问题来了:我什么时候用哪一种?需要看情况讨论:

场景用什么理由
普通字符串单引号 / 双引号选一个,保持一致,尽量选择双引号
文本里包含单引号双引号不用转义,直接写 "it's ok!"
文本里包含双引号单引号不用转义,直接写 '他说"你好"'
多行文本三引号换行、缩进都保留,不用写 \n

选一种引号作为主力(比如双引号),遇到冲突才换另一种。这样既统一,又能避免转义符号满天飞。

要避免的写法:msg = "it's ok" 和 msg = '他说"你好"' 混着用,项目里风格不一致。

# 推荐
name = "张三"
msg = "it's ok"         # 内含单引号,用双引号括住
msg = '他说"你好"'      # 内含双引号,用单引号括住

# 不推荐(风格不统一)
name = '张三'
msg = "it's ok"

关于三引号,你只要知道它能保留换行就行。遇到多行文本(比如 HTML 模板、SQL 语句)时再回来查。

1.2 转义字符:引号冲突的解决方式

如果你看这代码不顺眼,非要用单引号括住带单引号的文本,Python 也不是不能处理——它可以用 转义字符 来解决。

msg = 'it\'s ok'   # \' 表示“这不是结束符,这是个单引号字符”

\ 是转义符号,它的意思是:“把后面的字符当成普通字符,不要按语法规则解释。”

除了引号,\n 是换行,\t 是 Tab,\\ 是普通的反斜杠。

比如这样:

path = "C:\\Users\\name"   # 两个斜杠才表示一个斜杠

但问题是:这种写法可读性很差。所以项目内统一用双引号作为主力,就可以避免 90% 的反斜杠污染。

1.3 拼接字符串:+ 与隐式拼接

+ 运算符拼接

最直接的拼接方式是用 +:

first = "张"
last = "三"
full = first + last      # "张三"

But啊!如果你尝试拼接字符串和整数,解释器就不答应了,会报错:

age = 18
msg = "年龄:" + age   # TypeError: can only concatenate str (not "int") to str

这时候需要用 str() 把数字转成字符串:

msg = "年龄:" + str(age)   # "年龄:18"

是的没错,常见类型都有对应的函数,能将其他类型转换过来

Python 特有的隐式拼接

Python 有一个不多见但实用的特性:相邻的字符串字面量会自动拼接。

比如这样:

text = "Hello" " " "World"   # "Hello World"

这个特性通常用来拆分长字符串,方便阅读:

long_text = ("这是第一行长文本"
             "这是第二行长文本"
             "这是第三行长文本")

注意:这种拼接只对字面量有效,对变量无效。

a = "Hello"
b = "World"
c = a b     # SyntaxError: invalid syntax(不行)

那什么是字面量呢?字面量(Literal),你可以理解为定义变量时,等号右侧写的东西,也就是值,比如 42、17,这是整数字面量,"Hello"、"World",这是字符串字面量。

1.4 为什么需要掌握这些?

  • 引号冲突:字符串里包含引号时,你知道怎么处理而不报错。
  • + 拼接:最基础的组合方式,后面写日志、拼消息时天天用。
  • 隐式拼接:偶尔能帮你把长字符串拆成多行,让代码更干净

1.5 实操演练

第一步:看例子

先观察下面这段代码,注意每一行的写法:

# 1. 创建字符串
name = "张三"
print(name)

# 2. 处理引号冲突
msg1 = "it's ok"
msg2 = '他说"你好"'
print(msg1, msg2)

# 3. 拼接字符串
first = "张"
last = "三"
full = first + last
print(full)

# 4. 数字转字符串后拼接
age = 18
print("年龄:" + str(age))

注意观察:msg1 为什么能用双引号包住带单引号的文本?msg2 为什么能用单引号包住带双引号的文本?str(age) 把数字变成了什么?

第二步:猜结果

不看上面的输出,先猜一下每行 print() 会打印什么,写在注释后面:

print(name)          # 我猜:____
print(msg1, msg2)    # 我猜:____
print(full)          # 我猜:____
print("年龄:" + str(age))  # 我猜:____

然后把代码敲进编辑器跑一遍,看看你的猜测对不对。如果猜错了,想想为什么。

第三步:自己写

完成以下三个小任务:

任务1:定义一个变量 city,值是“北京”,然后打印它。

任务2:把 msg1 = "it's ok" 改成用单引号包住的写法(也就是 'it\'s ok'),看看输出有没有变。

任务3:定义一个变量 score = 95,打印 "你的分数是:95"。提示:不能直接写 "你的分数是:" + score,你知道为什么吗?

1.6 本节小结

  • 三种引号:单引号、双引号、三引号。选一种做主力,另一种处理冲突,三引号保留多行格式。
  • 转义字符:\ 让特殊字符变普通字符,但能不用就不用——靠引号策略避免转义更干净。
  • 拼接方式:+ 是主力,只能拼字符串,数字要先用 str() 转。隐式拼接是 Python 特性,用来拆行长文本。

下一节我们讲字符串的索引与切片——怎么从一串字符里“剪”出你需要的那一段。

2. 字符串的工具

本节目标:掌握字符串的常用方法,了解索引和切片
预计用时:20~25 分钟
完成标志:能熟练使用 len()、find()、split()、join() 等常用字符串方法,能掌握切片的技巧

上一节我们学会了怎么创建字符串、怎么把它们拼在一起。但实际开发中,你更多时候需要的是从字符串里找东西、切东西、改东西——就像厨师拿到食材后,需要用刀、镊子、漏勺来处理它。

这一节我们就来认识处理字符串的几件常用工具。

2.1 len()函数:字符串的“尺子”

想知道一个字符串有多长(有多少个字符),用 len():

name = "张三"
print(len(name))        # 2

msg = "Hello, 世界!"
print(len(msg))         # 10(逗号、空格、感叹号都算)

len() 是个通用函数,后面学列表、字典时也用它。

2.2 in运算符:判断存在

想检查某段文字是否在字符串里,用 in:

text = "我喜欢用 Python 写代码"
print("Python" in text)    # True
print("Java" in text)      # False

它返回的是 True 或 False,很适合放在 if 条件里。

2.3 find()函数:字符串的“地图”

想找到某段文字在字符串里的位置,用 find():

text = "Hello, world!"
print(text.find("world"))    # 7(w 在第 7 个位置)
print(text.find("Python"))   # -1(找不到就返回 -1)

注意:位置从 0 开始数。-1 表示“没找到”。

2.4 split()函数:字符串的“菜刀”

想把一个字符串按某种规则切成多段,用 split():

s = "苹果,香蕉,橙子"
fruits = s.split(",")        # ["苹果", "香蕉", "橙子"]
print(fruits[0])             # 苹果

不传参数时,默认按空白(空格、换行、Tab)切分:

words = "hello world python".split()    # ["hello", "world", "python"]

split() 返回的是列表(后面会专门讲,现在只需要知道“它能切出一组数据”)。

2.5 join()函数:字符串的“胶水”

split() 的反向操作——把一组字符串拼成一个:

fruits = ["苹果", "香蕉", "橙子"]
s = ",".join(fruits)          # "苹果,香蕉,橙子"

注意写法:分隔符写在前面,要拼接的列表写在括号里。

2.6 strip()函数:去除空白

处理用户输入时,经常会多出多余的空格,用 strip() 可以去掉首尾的空白:

user_input = "  你好  "
clean = user_input.strip()
print(clean)              # "你好"

还有 lstrip()(掐头)和 rstrip()(去尾),需要时再查就行。

2.7 replace()函数:替换

想把字符串里的某段文字换成别的,用 replace():

text = "我喜欢用 Python"
new_text = text.replace("Python", "C嘎嘎")
print(new_text)           # "我喜欢用 C嘎嘎"

2.8 按位置取字符:索引

字符串是有序序列,每个字符都有固定位置。位置从 0 开始编号,这叫索引。请务必记住,字 符 串 索 引 从 0 开 始!

s = "Python"
#    ↑↑↑↑↑↑
#    012345

print(s[0])    # P
print(s[3])    # h
print(s[5])    # n

负数索引表示从末尾开始数,-1 是最后一个字符:

print(s[-1])   # n
print(s[-3])   # h
print(s[-6])   # P

索引超出范围会报错:s[100] → IndexError

2.9 截取片段:切片

想取出一段连续的内容,用切片。语法是 [起始:结束:步长],顾头不顾尾。一定要记住:[0:5]实际取的是 0~4!

s = "Hello, world!"
#    0 1 2 3 4 5 6 7 8 9 10 11 12

print(s[0:5])      # Hello(取索引 0 到 4)
print(s[7:12])     # world(取索引 7 到 11)

省略写法:

print(s[:5])       # Hello(开头到 4,等价于 s[0:5])
print(s[7:])       # world!(7 到末尾)
print(s[:])        # 完整复制一份

负数切片同样有效:

print(s[-6:-1])    # world(-6 到 -2)
print(s[-6:])      # world!(-6 到末尾)

步长可以控制取的方向和间隔:

print(s[::2])      # Hlo ol!(每隔一个取一个)
print(s[::-1])     # !dlrow ,olleH(倒序,常用)

2.10 实操一下!

先写出你的猜想,再放入编辑器验证:

# 1. 索引
s = "Python"
print(s[0])      # ?
print(s[-1])     # ?
# print(s[10])   # 会发生什么?

# 2. 切片
s = "Hello, world!"
print(s[0:5])    # ?
print(s[7:])     # ?
print(s[::-1])   # ?

# 3. len()
print(len("你好世界"))  # ?

# 4. in
print("Py" in "Python")   # ?
print("java" in "Python") # ?

# 5. find()
print("Hello".find("e"))   # ?
print("Hello".find("a"))   # ?

# 6. split()
csv = "apple,banana,orange"
print(csv.split(","))      # ?

# 7. join()
words = ["a", "b", "c"]
print("-".join(words))     # ?

# 8. strip()
msg = "   hello   "
print(msg.strip())         # ?

# 9. replace()
msg = "I like Java"
print(msg.replace("Java", "Python"))  # ?

2.11 本节小结

工具作用示例
索引 s[i]按位置取一个字符"Hello"[1] → "e"
切片 s[start:end:step]截取一段"Hello"[1:4] → "ell"
len()测量长度len("Hello") → 5
in判断是否存在"Py" in "Python" → True
find()查找位置"Hello".find("e") → 1
split()切分成列表"a,b".split(",") → ["a","b"]
join()拼接成字符串",".join(["a","b"]) → "a,b"
strip()去除首尾空白" hi ".strip() → "hi"
replace()替换内容"Hi".replace("i","o") → "Ho"

3. 特殊的字符串

本节目标:了解几种带前缀的特殊字符串
预计用时:15~20 分钟(不含 f-string)
完成标志:能区分 f、r、t、b、u 五种字符串前缀的用途,并知道什么时候该用哪一个

如果你身边有写 Python 的朋友,那你可能见过这样的代码:

meta = f"名字:{name},年龄:{age},职业:{job}"

你可能会想,这我熟悉!meta 是一个字符串,可是前面那个 f 是什么意思?比较好看吗?

其实,Python 的字符串还有一些带前缀的变体——它们在引号前面加一个字母,改变字符串的行为或含义。

这一节我们来认识五种前缀:f、r、t、b、u。

3.1 r-string

r 是 raw(原始)的缩写。在字符串前面加 r,反斜杠 \ 不再被当作转义符,而是当成普通字符原样保留。

先看普通字符串的问题:

path = "C:\new\file.txt"
print(path)   # \n 被当成换行了,输出乱掉

\n 被解释成了换行,路径就乱了。用 r 可以避免这个问题:

path = r"C:\new\file.txt"
print(path)   # C:\new\file.txt 原样输出

r-string 最常见的用途有两个:

文件路径(尤其是 Windows 路径):

path = r"C:\Users\name\Documents"

正则表达式:正则里大量使用 \,用 r-string 可以少写一堆转义符。

import re
pattern = r"\d{3}-\d{4}"   # 匹配类似 123-4567 的格式

注意:r-string 不能以奇数个反斜杠结尾。比如 r"C:\" 会报错,需要写成 r"C:\" "\\" 或者干脆不用 r。

3.2 t-string:模板字符串(Python 3.15+)

注意:Python 3.15 目前未正式发布,了解即可

t 是 template(模板)的缩写。这是 Python 3.15 的新特性,由 PEP 750 引入。

t-string 的语法和 f-string 几乎一样——用 t 前缀代替 f 前缀,花括号里写表达式。

pi = 3.14
t't-strings are new in Python {pi}'

但关键区别在于:f-string 会立即计算并返回一个字符串;而 t-string 返回的是一个 Template 对象,不会立即求值。

可以这样理解:

  • f-string:厨师做好菜直接端上来,你马上就能吃。
  • t-string:厨师给你一份菜谱(Template 对象),你拿到后可以自己决定什么时候做、怎么做、甚至加什么料。

这种“延迟处理”的设计让 t-string 在以下场景特别有用:

  • SQL 安全:可以安全地处理插值,防止 SQL 注入
  • HTML 模板:自动转义,防止 XSS 攻击
  • Shell 命令:安全地拼接命令参数
  • 自定义 DSL:你可以自己决定如何处理插值

由于 t-string 是 Python 3.15 的新特性,目前大多数环境还不支持。现阶段你只需要知道它的存在和基本概念,等 3.15 普及后再深入使用。

那么问题来了,什么是 f-string ?不必着急,小节 3.4 将深入讲解

3.3 b-string:字节串

b 是 bytes(字节)的缩写。在字符串前面加 b,得到的是 bytes 对象,而不是普通的 str 字符串。

s = "hello"        # str 类型
b = b"hello"       # bytes 类型
print(type(s))     # 
print(type(b))     # 

bytes 对象里存放的是原始字节(0~255 的整数),而不是 Unicode 字符。它主要用于:

  • 文件读写(二进制模式)
  • 网络传输(发送和接收原始数据)
  • 与 C 语言库交互
# 写入二进制文件
with open("image.jpg", "wb") as f:
    f.write(b"\xff\xd8\xff\xe0")   # JPEG 文件头

# 字符串和 bytes 互转
text = "你好"
bytes_data = text.encode("utf-8")   # str → bytes
text_back = bytes_data.decode("utf-8")  # bytes → str

注意:b-string 里只能写 ASCII 字符(字母、数字、标点),不能直接写中文。要表示中文需要先 encode。

3.4 f-string:嵌入变量

本小节目标:了解 f-string 的用途,学会使用 f-string
预计用时:20~25 分钟
完成标志:能够运用 f-string 来拼接字符串

3.4.1 f-string 是什么?

f-string 的全称是 格式化字符串字面量(formatted string literals),是 Python 3.6 引入的一种字符串写法。

它的语法很简单:在字符串前面加一个字母 f,然后在字符串内部用 {变量名} 把变量嵌进去。

比如这样:

name = "张三"
age = 18
print(f"我叫{name},今年{age}岁")

这种写法直观、简洁、性能也好。前面我们学过的 + 拼接,写起来像是“把碎片粘在一起”,而 f-string 更像“在一个完整的句子里留出空位填东西”——后者更接近人类自然的表达方式。

3.4.2 为什么需要格式控制?

你可能觉得奇怪:变量嵌进去就行了,为什么还要专门学“格式控制”?

因为现实世界的数据展示没那么简单。

比如你从数据库里拿到一个商品价格 19.9,直接 f"价格:{price}" 输出是 价格:19.9——这没问题。但如果价格是 19.900000000000002(浮点数运算的经典误差),直接输出就难看了。

再比如你想做一个成绩单:

name = "张三"
score = 98.5
print(f"{name}:{score}分")

看起来还行。但如果名字长度不一样:

name1 = "张三"
name2 = "李小明"
print(f"{name1}:{score1}分")
print(f"{name2}:{score2}分")

输出会参差不齐:

张三:98.5分
李小明:97.0分

这时候你可能希望它们对齐,方便阅读。格式控制就是用来解决这类问题的——它让你能精确控制数字的小数位数、文本的对齐方式、千位分隔符、甚至进制显示。

简单说:值管“存什么”,格式管“怎么显示”。

3.4.3 花括号里可以放什么

f-string 的花括号 {} 里可以放各种 Python 表达式,比如:

name = "张三"
age = 18

print(f"我叫{name}")                # 变量
print(f"明年{age + 1}岁")           # 算术运算
print(f"我的名字有{len(name)}个字")  # 函数调用
print(f"我叫{name.upper()}")       # 方法调用
print(f"{[1,2,3][0]}")            # 索引取值

3.4.4 格式控制

f-string 的格式控制写在花括号里,用冒号 : 分隔变量名和格式说明符。基本结构是:{变量名:格式说明符}。

pi = 3.1415926
print(f"{pi:.2f}")   # 3.14
#        ↑ ↑
#     变量名 格式说明符(冒号后面全是格式控制)

下面按功能分组,逐一讲解:

一、对齐与填充

控制字符串在指定宽度内的对齐方式和填充字符。语法:{变量:填充符 对齐方式 宽度},但注意顺序是固定的:填充符 → 对齐符 → 宽度。

对齐符含义
<左对齐
>右对齐
^居中对齐
=数字填充(仅在数字类型有效,填充符写在符号和数字之间)
name = "Python"

print(f"{name:<10}")   # 'Python    '(左对齐,宽度10)
print(f"{name:>10}")   # '    Python'(右对齐)
print(f"{name:^10}")   # '  Python  '(居中)

指定填充字符(默认是空格):

print(f"{name:*<10}")   # 'Python****'
print(f"{name:*>10}")   # '****Python'
print(f"{name:*^10}")   # '**Python**'

= 对齐符单独说明一下。它主要用于数字,作用是让填充字符出现在符号(+/-)和数字之间,而不是数字前面。适用于带符号的数值对齐场景。默认情况下,<、>、^ 会把填充符放在符号前面;而 = 会把填充符放在符号和数字之间:

num = -42
print(f"{num:0=6}")    # '-00042'(0 填在负号和数字之间)
# 不加 = 的话:print(f"{num:0>6}")  # '000-42'(0 填在最前面,负号被挤到后面去了)

二、符号控制

控制正负号的显示方式:

符号含义
+正负数都显示符号
-仅负数显示符号(默认行为)
正数前显示空格,负数前显示负号
pos = 42
neg = -42

print(f"{pos:+}")    # '+42'
print(f"{neg:+}")    # '-42'
print(f"{pos: }")    # ' 42'(正数前有一个空格,用于对齐正负号)
print(f"{neg: }")    # '-42'

三、数字格式化

小数精度

用 .n 控制浮点数的小数位数:

pi = 3.1415926
print(f"{pi:.0f}")   # 3(不保留小数,四舍五入)
print(f"{pi:.2f}")   # 3.14
print(f"{pi:.5f}")   # 3.14159
千位分隔符

用 , 或 _ 作为千位分隔符:

num = 1234567890
print(f"{num:,}")    # 1,234,567,890(逗号分隔)
print(f"{num:_}")    # 1_234_567_890(下划线分隔,适合大数字)
百分比

用 % 把小数转成百分比显示:

ratio = 0.875
print(f"{ratio:.1%}")    # 87.5%
print(f"{ratio:.0%}")    # 88%

四、进制转换

用格式类型控制数字的显示进制:

类型含义示例(数字 255)
b二进制11111111
o八进制377
d十进制(默认)255
x小写十六进制ff
X大写十六进制FF
num = 255
print(f"{num:b}")    # 11111111
print(f"{num:o}")    # 377
print(f"{num:x}")    # ff
print(f"{num:X}")    # FF

加 # 前缀可以显示进制符号(0b、0o、0x):

print(f"{num:#b}")   # 0b11111111
print(f"{num:#x}")   # 0xff

五、指数表示法

用于表示非常大或非常小的数:

类型含义
e小写科学计数法(如 1.23e+10)
E大写科学计数法(如 1.23E+10)
g自动选择普通表示或科学计数法(小写 e)
G自动选择普通表示或科学计数法(大写 E)
num = 12345678.9
print(f"{num:e}")    # 1.234568e+07
print(f"{num:E}")    # 1.234568E+07
print(f"{num:.2e}")  # 1.23e+07

六、日期时间格式化

f-string 支持 datetime 对象 的格式化,格式符和 strftime 一致:

from datetime import datetime
now = datetime.now()

print(f"{now:%Y-%m-%d}")           # 2026-08-11
print(f"{now:%Y年%m月%d日}")        # 2026年08月11日
print(f"{now:%H:%M:%S}")           # 14:30:25
print(f"{now:%Y-%m-%d %H:%M}")     # 2026-08-11 14:30

完整格式符参考(常用):

格式符含义示例
%Y四位年份2026
%m两位月份08
%d两位日期11
%H24小时制小时14
%M分钟30
%S秒25
%A星期几(全称)Monday
%a星期几(缩写)Mon
%B月份(全称)August
%b月份(缩写)Aug

七、格式控制完整语法

把上面所有内容汇总成一张图:

{变量名: [填充符][对齐方式][宽度][,][.精度][类型]}
部分可选值说明
填充符任意字符(默认空格)用于填充空位
对齐方式< > ^ =左/右/居中/符号后填充
宽度数字输出最小宽度
千位分隔符, 或 _数字千位分隔
精度.n小数位数
类型b o d x X e E f F % g G格式类型

3.4.5 实操验证

# 1. 对齐
name = "Python"
print(f"{name:*<10}")   # ?
print(f"{name:*>10}")   # ?
print(f"{name:*^10}")   # ?

# 2. 符号控制
pos = 42
neg = -42
print(f"{pos:+}")       # ?
print(f"{neg:+}")       # ?

# 3. 小数精度
pi = 3.1415926
print(f"{pi:.2f}")      # ?
print(f"{pi:.4f}")      # ?

# 4. 千位分隔符
print(f"{1234567890:,}")  # ?
print(f"{1234567890:_}")  # ?

# 5. 百分比
print(f"{0.875:.0%}")   # ?

# 6. 进制转换
print(f"{255:b}")       # ?
print(f"{255:#x}")      # ?

# 7. 科学计数法
print(f"{12345678.9:.2e}")  # ?

# 8. 日期时间
from datetime import datetime
now = datetime.now()
print(f"{now:%Y-%m-%d %H:%M}")  # ?

3.4.6 f-string 完整速查表

用途写法示例输入输出
保留两位小数{x:.2f}3.141593.14
左对齐,宽度10{x:<10}"Hi""Hi "
右对齐,宽度10{x:>10}"Hi"" Hi"
居中,宽度10{x:^10}"Hi"" Hi "
填充星号居中{x:*^10}"Hi""****Hi****"
千位逗号分隔{x:,}1234567"1,234,567"
显示正负号{x:+}42"+42"
百分比{x:.0%}0.875"88%"
二进制{x:b}255"11111111"
十六进制(带前缀){x:#x}255"0xff"
科学计数法{x:.2e}12345678.9"1.23e+07"
日期时间{%Y-%m-%d}datetime.now()"2026-08-11"

3.4.7 f-string 本小节总结

f-string 是 Python 3.6 引入的一种字符串写法,在字符串前面加 f,用 {变量名} 嵌入值。它的核心优势是直观——你不需要用 + 把碎片拼起来,而是直接在一句话里留空填东西。

这一小节讲了 f-string 的几层用法:

  • 基础:花括号里放变量名、算术运算、函数调用、方法调用,任意表达式都行。
  • 格式控制:用冒号 : 指定对齐、填充、宽度、小数精度、千位分隔符、进制转换、科学计数法、百分比、日期时间格式——基本上你能想到的显示格式,f-string 都直接支持。

你不需要记住所有格式符。遇到“我要把这个数字显示成特定格式”的需求时,回来查速查表就行。用多了自然记住常用的那几种。

总结:日常写字符串,先想 f-string。它比 + 拼接更直观,比传统格式化方法更简洁,是 Python 现在推荐的首选字符串格式化方式。

3.5 u-string:Python 2 时代的遗产

u 是 Unicode 的缩写。在字符串前面加 u,表示这是一个 Unicode 字符串。

s1 = "你好"
s2 = u"你好"
print(s1 == s2)   # True

在 Python 3 中,所有字符串默认就是 Unicode,所以 u 前缀实际上什么也不做。你加不加,结果完全一样。

那为什么还要提它?两个原因:

第一,为了兼容 Python 2。 在 Python 2 中,普通字符串是字节串(类似 Python 3 的 b-string),处理中文时需要加 u 才能正确处理。所以如果你看到一些老代码里到处都是 u"...",那是因为它们要兼容 Python 2。

第二,为了“认识它”。 你在阅读老项目或第三方库源码时,可能会遇到 u"text" 这种写法。知道它是什么、知道它现在没作用,就不会被它搞懵。

如果你不需要兼容 Python 2,永远不需要主动写 u-string。它只是一个历史遗留标记。

3.6 前缀组合

某些前缀可以组合使用,同时具备两种前缀的特性。

常见的组合:

组合含义示例
rf 或 fr原始格式化字符串rf"C:\Users\{name}"
rb 或 br原始字节串rb"\x89PNG"

rf / fr:原样保留反斜杠 + 嵌入变量

r 让反斜杠原样保留,f 让花括号里的变量被替换。两者互不冲突,可以同时生效。

name = "LanWan"
path = rf"C:\Users\{name}\Documents"
print(path)   # C:\Users\LanWan\Documents

顺序不影响结果,fr"..." 和 rf"..." 等价。

rb / br:原始字节串

r 让反斜杠原样保留,b 生成 bytes 对象。主要用于处理包含反斜杠的二进制数据。

data = rb"\x89PNG\r\n\x1a\n"
print(data)   # b'\\x89PNG\\r\\n\\x1a\\n'

不能组合的前缀

f 不能和 b 组合,f 也不能和 u 组合:

fb"..."   # SyntaxError
fu"..."   # SyntaxError

因为 f-string 处理的是文本(str),而 b-string 处理的是字节(bytes),两者在底层数据模型上不兼容。u-string 在 Python 3 中无实际作用,组合也没有意义。

需要组合时,记住一个原则:r 可以和任何前缀组合,f 只能和 r 组合,b 只能和 r 组合。

3.7 实操一下

下面我们来验证 r、t、b、u 四种特殊字符串的行为。先写出你猜测的结果,再把代码敲进编辑器跑一遍。

# 1. r-string:路径中的反斜杠
path1 = "C:\new\file.txt"
path2 = r"C:\new\file.txt"
print(path1)   # ?
print(path2)   # ?

# 2. r-string:字符串结尾反斜杠的处理
# print(r"C:\")    # 取消注释,看看会发生什么
# print(r"C:\" "\\")  # 取消注释,看看这种写法能不能解决?

# 3. r-string:正则表达式中的用法
import re
# 用 r-string 写一个匹配电话号码的正则(3位-4位,如 123-4567)
pattern = r"\d{3}-\d{4}"
print(pattern)             # ?
print(re.match(pattern, "123-4567") is not None)  # ?

# 4. b-string:类型和内容
s = "hello"
b = b"hello"
print(type(s))    # ?
print(type(b))    # ?
print(b[0])       # ?(注意:b-string 索引返回的是整数还是字符?)

# 5. b-string:str 和 bytes 的转换
text = "中文"
bytes_data = text.encode("utf-8")
print(bytes_data)          # ?
text_back = bytes_data.decode("utf-8")
print(text_back)           # ?

# 6. b-string:b-string 里能不能写中文?
# b"中文"   # 取消注释,看看会发生什么

# 7. t-string:验证类型(Python 3.15+ 才支持)
# 如果你的 Python 版本低于 3.15,这一行会报错
# template = t"Hello {name}"
# print(type(template))     # ?

# 8. u-string:Python 3 中是否还有意义?
s1 = "你好"
s2 = u"你好"
print(s1 == s2)    # ?
print(type(s1))    # ?
print(type(s2))    # ?

把输出结果记录下来,和你的猜测对比。如果结果不一样,想想为什么。

3.8 本节小结

这一节我们认识了五种带前缀的字符串。它们本质上是同一种东西——字符串,只是在前面加了一个字母,行为就变了。

r-string:反斜杠原样保留,不转义。写 Windows 路径和正则表达式时最有用。

t-string:Python 3.15 的新特性,返回 Template 对象而不是字符串。现阶段了解即可,等 3.15 普及后再深入。

b-string:生成 bytes 对象而不是 str 对象。处理二进制数据(文件、网络)时才会用到。

f-string:日常使用频率最高的一个。在字符串里用 {变量名} 嵌入值,直观、简洁、性能好。花括号里可以放任何表达式,冒号后面可以跟格式控制符——对齐、填充、小数精度、千位分隔符、进制转换、科学计数法、百分比、日期时间格式化,基本上你需要的显示格式它都支持。

u-string:Python 2 时代的遗产。在 Python 3 中没有任何作用,你不需要主动写它,但看到老代码里有它,知道是什么意思就行。

一张表记住五种前缀

前缀全称作用什么时候用
rraw反斜杠原样保留文件路径、正则表达式
ttemplate返回 Template 对象Python 3.15+ 的安全模板场景
bbytes生成 bytes 对象二进制文件、网络传输
fformatted嵌入变量和表达式日常字符串拼接,首选
uUnicodePython 3 中无实际作用不用主动写,认识即可

前缀可以组合使用

某些前缀可以组合在一起,同时具备两种特性:

组合含义示例
rf 或 fr原始格式化字符串rf"C:\Users\{name}"
rb 或 br原始字节串rb"\x89PNG"

f 不能和 b 组合,也不能和 u 组合。原因很简单:f-string 处理的是文本(str),b-string 处理的是字节(bytes),两者底层数据模型不同,没法混用。

总结

  • 日常写代码:用 f
  • 路径和正则:用 r
  • 二进制数据:用 b
  • Python 3.15 以后:关注 t
  • u 不用管,认识就行

4. 本章小结

这一章我们从“字符串是什么”一路走到了“字符串能怎么玩”。回头看一下走过的路:

第0节:概念。 字符串是一串文本,Unicode 加持让你直接写中文,不可变意味着每次“修改”都是创建新字符串,有序序列意味着每个字符都有编号。

第1节:创建与拼接。 三种引号应对不同场景,转义字符解决冲突但能不用就不用,+ 和隐式拼接各有各的用处。

第2节:工具。 len() 量长度,in 查存在,find() 找位置,split() 切分,join() 拼接,strip() 去空白,replace() 替换,再加上索引和切片——这九样东西基本覆盖了你日常对字符串的所有操作。

第3节:特殊字符串。 r 留着反斜杠,t 是未来的模板,b 处理二进制,f 是日常首选,u 是历史遗迹。记住组合规则:r 是万能搭子,f 只搭 r,b 也只搭 r。

字符串的内容就到这里。它是你往后每一段代码都会遇到的东西,但它的核心规则并不多。记住一句话:字符串就是文本,工具都在本章,回来查就行。

5. 附录

5.1 转义字符速查表

转义写法实际字符
\'单引号
\"双引号
\\反斜杠
\n换行
\tTab
\r回车

5.2 常用字符串方法速查表

方法/操作作用示例结果
len(s)长度len("你好")2
s.find(sub)查找位置"hello".find("e")1
s.split(sep)切分"a,b".split(",")["a","b"]
sep.join(list)拼接",".join(["a","b"])"a,b"
s.strip()去首尾空白" hi ".strip()"hi"
s.replace(a,b)替换"hi".replace("i","o")"ho"

5.3 字符串前缀速查表

前缀全称作用
fformatted嵌入变量和表达式,立即求值
rraw反斜杠原样保留,不转义
ttemplate返回 Template 对象,延迟处理(3.15+)
bbytes生成 bytes 对象而非 str
uUnicodePython 3 中无实际作用

5.4 前缀组合规则速查表

组合含义示例
rf / fr原始格式化字符串rf"C:\{name}"
rb / br原始字节串rb"\x89PNG"
组合是否允许
f + r可以
f + b不可以
f + u不可以
b + r可以
b + u不可以

5.5 索引与切片速查表

写法含义示例结果
s[i]取第 i 个字符"Hello"[1]"e"
s[-i]取倒数第 i 个字符"Hello"[-1]"o"
s[start:end]取 start 到 end-1"Hello"[1:4]"ell"
s[start:]取 start 到末尾"Hello"[2:]"llo"
s[:end]取开头到 end-1"Hello"[:3]"Hel"
s[start:end:step]带步长切片"Hello"[::2]"Hlo"
s[::-1]倒序"Hello"[::-1]"olleH"

5.6 f-string 格式控制速查表

用途写法示例结果
保留两位小数{x:.2f}3.141593.14
左对齐,宽度10{x:<10}"Hi""Hi "
右对齐,宽度10{x:>10}"Hi"" Hi"
居中,宽度10{x:^10}"Hi"" Hi "
填充星号居中{x:*^10}"Hi""****Hi****"
千位逗号分隔{x:,}1234567"1,234,567"
显示正负号{x:+}42"+42"
百分比{x:.0%}0.875"88%"
二进制{x:b}255"11111111"
十六进制(带前缀){x:#x}255"0xff"
科学计数法{x:.2e}12345678.9"1.23e+07"
日期{%Y-%m-%d}datetime.now()"2026-08-11"

5.7 引号与转义速查表

场景推荐写法说明
普通文本"hello"双引号为主力
含单引号"it's ok"双引号避免转义
含双引号'他说"你好"'单引号避免转义
多行文本"""..."""保留换行和缩进
Windows 路径r"C:\Users"用 r-string 避免转义
正则表达式r"\d{3}"用 r-string 避免转义

5.8 类型与互转速查表

函数作用示例结果
str()转成字符串str(123)"123"
int()转成整数int("42")42
float()转成浮点数float("3.14")3.14
bool()转成布尔bool("")False
encode()字符串→字节"你好".encode()b'\xe4\xbd\xa0'
decode()字节→字符串b'\xe4\xbd\xa0'.decode()"你好"

5.9 字符串常用方法速查表(完整版)

方法作用示例结果
s.count(sub)统计子串出现次数"hello".count("l")2
s.startswith(prefix)是否以某字符串开头"hello".startswith("he")True
s.endswith(suffix)是否以某字符串结尾"hello".endswith("lo")True
s.upper()转大写"hi".upper()"HI"
s.lower()转小写"HI".lower()"hi"
s.title()每个单词首字母大写"hello world".title()"Hello World"
s.capitalize()首字母大写"hello".capitalize()"Hello"
s.swapcase()大小写互换"Hello".swapcase()"hELLO"
s.isdigit()是否全为数字"123".isdigit()True
s.isalpha()是否全为字母"abc".isalpha()True
s.isalnum()是否全为字母或数字"abc123".isalnum()True
s.isspace()是否全为空白字符" ".isspace()True
s.lstrip()去除左侧空白" hi ".lstrip()"hi "
s.rstrip()去除右侧空白" hi ".rstrip()" hi"
s.center(width)居中,两边填充空格"hi".center(5)" hi "
s.ljust(width)左对齐,右侧填充空格"hi".ljust(5)"hi "
s.rjust(width)右对齐,左侧填充空格"hi".rjust(5)" hi"
s.zfill(width)右侧对齐,左侧补零"42".zfill(5)"00042"
s.join(iterable)用 s 拼接可迭代对象"-".join(["a","b"])"a-b"
s.split(sep)按 sep 切分"a,b".split(",")["a","b"]
s.rsplit(sep)从右侧开始切分"a,b,c".rsplit(",",1)["a,b","c"]
s.partition(sep)按 sep 分成三部分"a-b-c".partition("-")("a","-","b-c")
s.rpartition(sep)从右侧按 sep 分成三部分"a-b-c".rpartition("-")("a-b","-","c")
s.find(sub)查找子串位置,找不到返回 -1"hello".find("l")2
s.rfind(sub)从右侧查找子串位置"hello".rfind("l")3
s.index(sub)查找子串位置,找不到报错"hello".index("e")1
s.rindex(sub)从右侧查找,找不到报错"hello".rindex("l")3
s.replace(old, new)替换所有匹配项"hi".replace("i","o")"ho"
s.strip()去除首尾空白" hi ".strip()"hi"
s.removeprefix(prefix)移除前缀"hello".removeprefix("he")"llo"
s.removesuffix(suffix)移除后缀"hello".removesuffix("lo")"hel"

下一章,我们进入条件判断与循环。下一章,我们进入条件判断与循环。它们让程序有了“脑子”——能根据情况做决定,能重复干活不嫌累。


by 澜湾
by LanWan