Skip to content
This page has been auto-translated and may contain errors.View in English

字符串

docs.scrimba.com

文本出现在几乎所有你编写的程序中。名字、消息、分数、标签。在 Python 中,任何一段文本都称为字符串:任何用引号括起来的值。单引号或双引号,两者的工作方式相同。

字符串是 Python 的主要文本类型。它们承载从用户名到 URL 路径再到格式化输出的一切。单引号和双引号产生相同的结果;选择是风格问题。

str 位于每个系统边界处:终端 I/O、文件内容、网络响应、序列化数据。它是 Python 的不可变(创建后无法更改)Unicode 序列类型。两种引号风格产生相同的对象,所以它们之间的选择纯粹是风格问题。

python
greeting = "Hello, world"
username = 'zhang'

引号选择重要的唯一时刻是当你的文本包含引号时。使用相反的风格,这样你就不必转义它们:

社区惯例是双引号。切换风格的实际原因是在内容包含该字符时避免转义:

惯例是双引号,格式化工具如 Black 和 Ruff 会自动执行。手工编写时唯一的原因是在内容包含分隔符时避免反斜杠转义:

python
note = "It's a great day"      # 内部包含撇号,使用双引号
message = 'She said "hello"'   # 内部包含双引号,使用单引号
escaped = "She said \"hello\""  # 或用反斜杠转义

不可变性

字符串是不可变的:创建后,无法更改。把字符串看作在创建的瞬间永久固定。任何看起来像在修改字符串的操作实际上是产生了一个全新的字符串。原始字符串保持完全不变。

字符串是不可变的:没有方法可以就地修改字符串。每个转换文本的操作都返回一个新字符串,并将原始字符串保持不变。实际的结果是,未分配到任何地方的方法调用对任何事物都没有影响。

str 对象是不可变的,这意味着它们的内容在创建后是固定的,任何东西都无法写入。这赋予字符串两个你实际使用的属性:它们是可散列的(其内容永不改变的值可以根据这些内容进行归档,这就是为什么字符串可以用作字典键或集合成员),并且它们可以安全地在名称之间传递而无需复制,因为没有人能在共享字符串的情况下改变它。

python
name = "zhang"
name = name.upper()   # "ZHANG" 是一个新字符串;"zhang" 保持不变

直接的结果:无法更改特定位置的字符。如果尝试,Python 会引发错误。

python
name = "zhang"
name[0] = "Z"   # TypeError: 'str' object does not support item assignment

要获得修改后的字符串,使用切片或方法构建一个新的。两者都将在下面介绍。

尝试字符赋值直接显示了约束:

python
name = "zhang"
name[0] = "Z"   # TypeError: 'str' object does not support item assignment

当需要修改后的版本时,标准工具是用连接进行切片以进行位置编辑,以及用 replace() 进行替换。两者都产生一个新字符串,并保持原始字符串不变。

分配给位置(name[0] = "Z")每次都会引发 TypeError,没有例外。对于位置编辑,通过在要更改的部分周围切片来构建新字符串:name[:1].upper() + name[1:] 将第一个字符大写。对于字符串中任何地方的替换,使用 replace()

Juno不可变性 字符串一旦存在就永不改变。任何看起来像在编辑的操作都会给你返回一个新字符串,所以分配结果否则它就会消失。早期抓住我的:name[0] = "Z" 不起作用,它会引发 TypeError
Juno不可变性 没有字符串方法就地编辑,每一个都返回一个新字符串并单独保持原始不变。所以 name.upper() 单独没有任何有用的作用,你必须分配它。而且 name[0] = "Z" 会引发 TypeError,没有就地字符交换。
Juno不可变性 不可变性使字符串可散列,所以它们可以作为字典键,并且可以安全共享而不需要复制。实际结果:分配每个转换方法的结果,因为没有人接触原始。对于位置编辑,在该位置周围切片并重建,因为项赋值总是引发 TypeError

索引和切片

字符串中的每个字符都有一个编号位置,从零开始。你可以通过将该位置号放在方括号中来读取单个字符。负数从末尾向后计数。

字符串是具有零基索引的序列。负索引从末尾计数。切片在单个表达式中提取任何连续范围,并且对于超出范围的值永不引发错误。

字符串的行为像一个有序序列,你可以对其进行索引和切片。读取单个位置(s[i])在索引指向末尾之外时引发 IndexError。切片(s[start:stop:step])行为不同:超出范围的边界被静默夹紧到存在的内容,所以切片永不会引发 IndexError。这个差异是实际的:索引访问严格,切片宽松。

python
word = "Python"
#       012345

print(word[0])    # "P"
print(word[2])    # "t"
print(word[5])    # "n"
print(word[-1])   # "n"  (最后一个字符)
print(word[-2])   # "o"  (倒数第二个)

-1 总是最后一个字符,-2 是倒数第二个,以此类推。当你想要字符串的末尾而不知道其确切长度时,它们很有用。

负索引换行:-1len(s) - 1-2len(s) - 2。当你不想手动计算长度时,对末尾锚定访问最有用。超出范围的负索引仍然会引发 IndexError,与正索引相同。

负索引在边界检查之前被转换为 len(s) + i,所以 -1 落在最后一个字符上,-2 落在它前面的那个上。超出范围会引发 IndexError,无论是正还是负:只有切片能原谅这一点,普通索引不会。

切片提取一个块。[start:stop] 包括 start 并排除 stop

python
word = "Python"

print(word[0:2])   # "Py"     (位置 0 和 1)
print(word[2:])    # "thon"   (位置 2 到末尾)
print(word[:3])    # "Pyt"    (开始到位置 2)
print(word[:])     # "Python" (整个字符串的副本)
print(word[::2])   # "Pto"    (每隔一个字符)
print(word[::-1])  # "nohtyP" (反转)

最常用的三种模式:word[:n] 用于前 n 个字符,word[n:] 用于位置 n 及以后的所有内容,word[-n:] 用于最后 n 个字符。word[::-1] 反转字符串。第一次看起来很奇怪,但这是惯用的 Python,你会经常看到它。

与直接索引不同,切片永不引发 IndexError。Python 会静默地夹紧超出范围的索引,所以在短字符串上 word[100:] 返回 "" 而不是崩溃。step 参数控制步幅:word[::2] 取每隔一个字符,word[::-1] 反向遍历。

s[start:stop:step] 中,任何留下的部分默认为"你要去的方向的自然结束",而不是固定的 0len()。使用正 step 意味着从开始到结束;使用负 step 它会翻转,所以 start 变成最后一个字符,stop 从前面跑开。这就是为什么 s[::-1] 在没有你写任何边界的情况下在整个字符串中反向行走,以及相同原因 s[::-2] 给你每隔一个字符向后。

Juno索引和切片 位置从零开始,所以 word[0] 是第一个字符,word[-1] 是最后一个。切片抓住一个范围:word[start:stop] 保持 start 并在 stop 之前停止。word[::-1] 反转字符串,第一次看起来很奇怪,然后你永远使用它。
Juno索引和切片 从零索引,或从末尾用负索引,其中 word[-1] 是最后一个字符。切片取一个包括 start 并排除 stop 的范围,以及第三个 step 设置步幅,所以 word[::-1] 反转。值得记住的陷阱:索引超过末尾会引发 IndexError,但切片超过它返回那里的内容而不是失败。
Juno索引和切片 普通索引严格并超出范围时引发 IndexError,切片宽松并夹紧到任何存在的东西,所以 word[100:] 返回 "" 而不是引发。留下切片部分,它默认为 step 方向的自然结束,这就是整个原因 word[::-1] 在没有写入边界的情况下反向。当你宁愿不在空的或短的输入上崩溃时,选择一个切片。

基本字符串方法

字符串配备了一套内置方法:可以直接在任何字符串值上调用的操作。你写字符串(或持有它的变量),然后一个点,然后方法名。每个方法返回一个新字符串。原始字符串永不更改。

字符串方法是附加到 str 类型的函数。因为字符串是不可变的,每个方法都返回一个新字符串而不是修改原始字符串。你不分配或传递到某处的方法调用没有持久影响。

每个转换方法返回一个新的 str 并保持原始不变,这直接来自不可变性。它们也在代码点上工作(字符本身,而不是用于存储它们的原始字节),所以方法在带有重音符号和非拉丁文本上正确运行,而你无需做任何特殊的事情。权衡要记住的:每个方法分配一个新字符串,所以在大文本上链接许多方法意味着许多分配。

大小写

python
text = "Hello, World"

text.lower()       # "hello, world"
text.upper()       # "HELLO, WORLD"
text.title()       # "Hello, World"  (每个单词大写)
text.capitalize()  # "Hello, world"  (仅第一个单词)

lower()upper() 是你将最频繁使用的两个。lower() 特别适用于比较文本:"Alice""alice" 在对两边调用 .lower() 后变成相同的东西。

lower() 是比较或存储前的标准规范化步骤。title() 使用粗略规则大写每个单词的首字母,在缩略语中出错:"it's" 变成 "It'S"。将其视为仅显示格式。

lower() 应用 Unicode 完整大小写转换。对于不区分大小写的比较,casefold() 更正确:它应用 lower() 跳过的其他转换(例如德语 ß 变成 ss)。title() 在任何非字母数字字符后大写,会误处理缩略语和带连字符的名字。对于正确的标题大小写,手动实现逻辑。

空白

python
text = "  hello  "

text.strip()    # "hello"    (两边)
text.lstrip()   # "hello  "  (仅左边)
text.rstrip()   # "  hello"  (仅右边)

strip() 从字符串的两端移除空格。几乎任何时候你处理用户输入或来自文件的文本时,你都会使用它,因为杂散的空格导致无声失败:"alice" != "alice "

strip() 从两端移除所有前导和尾随空白:空格、制表符和换行符。方向性变体让你只清理一侧,在仅剥离尾随换行符而不触及缩进时有用。所有三个都接受一个可选字符参数来剥离特定字符。

无参数时,strip() 从两端移除每种空白,包括非 ASCII 空白,而不仅仅是普通空格。使用字符参数时,它从末尾移除这些字符中的任何一个,这是陷阱:参数是要剥离的字符集,而不是要匹配的前缀。"xxhelloxx".strip("x") 返回 "hello",但 "https://".strip("https") 不会移除前缀 "https",它从两端剥离每个 htps 并返回 "://"。要移除已知的前缀或后缀,改为使用 removeprefix()removesuffix()

查找

python
text = "Hello, world"

text.find("world")         # 7
text.find("Python")        # -1  (未找到)
text.count("l")            # 3
text.startswith("Hello")   # True
text.endswith("world")     # True

find() 返回字符串内一段文本开始的位置。如果不在那里,它返回 -1。当你只关心字符串是否以特定的东西开始或结束时,使用 startswith()endswith()

find() 返回第一个匹配的起始索引,或 -1-1 惯例让你直接在切片或算术中使用结果而不用检查。startswith()endswith() 各接受一个字符串元组,所以你可以在一次调用中测试多个前缀或后缀。

find() 从左到右扫描,当没有匹配时返回 -1index() 做相同的搜索但引发 ValueError 而不是返回 -1:当缺少匹配意味着你的代码有错误并且你想在那里停止时选择 index(),当缺失是你将检查的常规输入时选择 find()。对于纯粹的"它是否以这个开始或结束"问题,startswith()endswith() 说出你的意思并在第一个不匹配时停止,所以比 find()in 检查更优选该工作。

替换

python
text = "Hello, world"

text.replace("world", "Python")   # "Hello, Python"
text.replace("l", "L")            # "HeLLo, worLd"  (所有出现)
text.replace("l", "L", 1)         # "HeLlo, world"  (仅第一个)

replace() 用另一段文本交换一段文本的每个出现,并给你返回一个新字符串。原始不更改。如果你只想替换第一个出现,传递第三个参数。

replace() 默认替换所有非重叠出现。count 参数限制有多少被替换。由于它返回一个新字符串,调用可以链接:text.replace("a", "A").replace("e", "E") 依次应用两个替换。

replace() 匹配文字子字符串,而不是模式,所以它没有正则表达式并将参数中的每个字符视为自身。它从左到右替换非重叠匹配,可能让你吃惊:"aaa".replace("aa", "b")"ba",而不是 "bb",因为第二个 aa 与第一个匹配重叠。count 参数限制有多少被替换,由于每次调用都返回一个新字符串,你可以链接它们:text.replace(",", "").replace(" ", "_")

分割和连接

split() 在分隔符处将字符串切成块并以列表形式返回。你告诉它要切什么:

split() 在分隔符处分割并以列表形式返回段。无参数调用时,它在任何空白运行上分割并丢弃来自多个连续空格的空字符串:

split(sep) 从左到右扫描,在每个 sep 的非重叠出现处分割。无参数时,它使用不同的算法:它在任何空白运行上分割并从结果中删除前导和尾随空白。rsplit(sep, n) 从右分割,适用于隔离点缀路径或命名空间标识符的最后一段。

python
csv_row = "Zhang,28,Beijing"
parts = csv_row.split(",")     # ["Zhang", "28", "Beijing"]

"  hello   world  ".split()   # ["hello", "world"]

split() 返回一个列表,一个有序的值序列。它们有自己的列表章节;现在将它们视为 split() 产生和 join() 消费的部分序列。

join() 做相反的:它将一个字符串列表组合成一个。.join() 之前的字符串被放在每个项之间:

python
words = ["Hello", "world"]

" ".join(words)    # "Hello world"
", ".join(words)   # "Hello, world"
"".join(words)     # "Helloworld"

要记住的模式:separator.join(list_of_strings)。分隔符在左边,列表在右边。" ".join(words) 在每个单词之间放一个空格。"".join(words) 中间什么都没有地粘合它们。

join() 是任何时候你从多个部分组装单个字符串时的正确工具。它执行单个分配而不是在每个步骤创建新字符串。对于两个或三个字符串,+ 完全没问题。一旦你有任何大小的列表,选择 join()

join() 遍历片段一次,计算总大小,分配结果一次,并将所有东西写入。使用重复的 + 在循环中构建相同的字符串是昂贵的形状:因为字符串无法更改,每个 + 分配一个新字符串并将到目前为止的所有东西复制到它中,所以成本随着片段数量的平方增长。在少数几个字符串中,没有人注意,但在数千个上,它将快速操作变成缓慢。防止它的规则:收集片段到列表中并在末尾一次 join(),永不在循环中 += 字符串。

Juno基本字符串方法 这些中的每一个都返回一个新字符串,所以分配结果或它就消失了。你每天会用到的少数几个:.lower().upper() 用于大小写,.strip() 用于修剪杂散空格,.find() 来定位文本(当找不到时返回 -1),.replace() 来交换文本,以及 .split() 配合 sep.join() 来将字符串分开并重新组装。
Juno基本字符串方法 所有这些都返回一个新字符串,没有人变异,所以链接它们或分配它们。保持陷阱直线:.strip("x") 取一组字符来修剪,而不是一个前缀,以及 .split() 无参数会折叠空白运行。对于组装许多片段,构建一个列表并用 sep.join() 它。
Juno基本字符串方法 在真实代码中咬人的两个陷阱:.strip(chars) 从末端剥离字符集,而不是前缀,所以当你指前缀时使用 removeprefix(),以及使用 += 在循环中构建字符串是缓慢形状,所以收集到列表中并一次 join()。对于比较,使用 .casefold() 超过 .lower() 进行规范化,并将 .title() 视为仅显示,因为它会破坏缩略语。

f-字符串

f-字符串直接在文本内嵌入值。在开始引号前放 f,然后将任何变量或表达式包装在花括号中。Python 在代码运行时填充它。你也可以在值后面添加冒号来控制它的显示方式。

f-字符串在运行时计算 {} 内的任何表达式并将结果转换为字符串。{} 内的冒号引入格式规范:用于控制小数位、对齐和数字格式的紧凑语法。

f-字符串中的每个 {} 最后都会调用该值自己的 __format__ 方法(一个dunder,当 Python 格式化值时在幕后调用的双下划线方法),传递沿着你在冒号后写的任何东西。所以你写的任何类都可以通过定义 __format__ 来决定它在 f-字符串内如何出现。转换标志 !r!s!a 首先在值上运行 repr()str()ascii()!r 是要保持的,因为它以你在代码中键入它的方式显示值,包括引号和转义,这在下面的实践中显示。

python
name = "Zhang"
score = 94.5

print(f"Hello, {name}!")           # "Hello, Zhang!"
print(f"Score: {score:.1f}%")      # "Score: 94.5%"
print(f"2 + 2 = {2 + 2}")          # "2 + 2 = 4"
print(f"Name: {name.upper()}")     # "Name: ZHANG"

冒号后的格式规范控制值的显示方式:

规范含义示例
.2f2 位小数f"{3.14159:.2f}""3.14"
.0%百分比,无小数f"{0.94:.0%}""94%"
,千位分隔符f"{1000000:,}""1,000,000"
>10在 10 个字符中右对齐f"{'hi':>10}"" hi"

你会最频繁地使用 .2f:任何时候你显示小数并想要整洁的数字而不是很长的数字运行。表中的所有其他内容在你需要时都在那里。你可以在 {} 内放任何变量、算术或方法调用。

.2f.0% 涵盖大多数显示格式。对齐指定符(><^)与宽度结合时产生表格输出。常规模式是 {value:[align][width][.precision][type]}。一旦你识别了片段,任何规范都可以读取而无需记住所有组合。

相同的规范对不同的类型可能意味着不同的东西,因为每种类型都格式化自己:f"{'hi':5}" 在右边填充文本而 f"{42:5}" 在左边填充数字,相同 :5,相反结果。值得养成习惯的转换标志是 !r:它在格式化之前运行 repr(),它用引号包装字符串并将不可见字符(制表符、尾随空格、换行符)变成可见转义序列。当输出看起来有微妙的错误时,将 {value} 交换为 {value!r} 是最快的方式来看实际里面有什么。

Junof-字符串 在开始引号前放 f,然后将任何变量、求和或方法调用包装在 {} 中,Python 在行运行时将结果放进去。{} 内的冒号控制外观::.2f 用于两位小数是你将依赖的那个。远比用 + 粘合文本更整洁。
Junof-字符串f"..." 计算 {} 中的任何表达式并直接放下结果,无需 str()。冒号后来格式规范::.2f:,:>10 和朋友,所有遵循 {value:[align][width][.precision][type]}。学一次部分,你可以阅读任何规范而不用记住它们。
Junof-字符串 每个 {} 调用该值自己的 __format__,所以冒号后的规范意味着那种类型决定的任何东西,这就是为什么 :5 在相反方向上填充文本和数字。要保持的标志是 !r:它以你会键入它的方式显示值,所以杂散空白和其他不可见的垃圾在输出看起来离开的那刻跳出来。

多行字符串

要写跨越多行的字符串,使用三个引号:开始处三个 " 和末尾三个。Python 完全按照你写它们的方式保留所有换行符和间距。

三引号字符串逐字保留所有空白和换行符。它们是电子邮件模板和 SQL 查询等长文本块的标准,以及文档字符串:放在函数或类正文开始处的内联文档。

三引号字符串完全按照编写的方式保留每个字符,包括每行上的前导空格,这是陷阱:缩进文字以匹配你的代码,那个缩进就登陆在文本内部。当它是函数、类或模块中的第一个语句时,Python 将其保留为该对象的文档字符串help() 为其显示的文档),所以惯例是在那里放一个简短的总结。要缩进三引号块以匹配其周围而不让缩进泄漏到值中,在运行时用 textwrap.dedent() 剥离它。三 '''""" 工作相同;""" 是惯例。

python
message = """
Dear Zhang,

Thank you for your order.

Best regards,
The Team
"""
Juno多行字符串 开始和结束处三个引号让字符串跨越多行,Python 完全按照你键入的方式保留每个换行符和空格。当你有文本块(如消息或模板)不舒适地适应一行时,选择它们。
Juno多行字符串 三引号逐字保留换行符和间距,所以它们适合电子邮件模板、SQL 和任何长块。它们也是文档字符串工作方式:函数或类顶部的三引号字符串变成其内联文档。看好缩进,因为你添加来排列它的空格最终在文本内。
Juno多行字符串 三引号内的所有东西都被逐字保留,前导空白包括,这是陷阱:缩进文字以匹配你的代码,那个缩进登陆在值中。在函数或类的顶部放一个简短的,它变成文档字符串,help() 显示。当你需要它在源中缩进但在值中干净时,通过 textwrap.dedent() 运行它。

转义序列

一些字符很难直接在字符串内键入。Python 使用转义序列:一个反斜杠后跟一个代表某些东西的字母。你将不断使用的两个是 \n 用于新行和 \t 用于制表符。

转义序列让你嵌入会破坏语法或无法直接键入的字符。你会用到的那些:\n(换行)、\t(制表符)、\\(文字反斜杠)、\"\'(在匹配分隔符字符串内的引号)。Windows 路径需要反斜杠,它与转义处理冲突。用 r 前缀来禁用它。

除了日常的 \n\t,Python 支持 Unicode 转义来处理你无法直接键入的字符:\uXXXX\UXXXXXXXX 按其数字命名一个字符,以及 \N{name} 用文字命名它,像 \N{GREEK SMALL LETTER ALPHA}。在真实代码中要用的那个是原始字符串,写成 r"...",它完全关闭转义处理并不变地通过每个反斜杠。用它用于 Windows 路径和用于正则表达式模式,反斜杠被意思给那个消费者而不是给 Python,以及当忘记 r 时产生无声地匹配错误东西的模式。

序列字符
\n换行
\t制表符
\\文字反斜杠
\"双引号
\'单引号
python
print("Line one\nLine two")        # 两行输出
print("Name:\tZhang")              # Name:   Zhang
path = r"C:\Users\Zhang\Documents" # 原始字符串,无转义处理
Juno转义序列 字符串内的反斜杠意思是"特别读下一个字符":\n 开始新行,\t 插入制表符,\\ 是一个真正的反斜杠。那两个,\n\t,是你实际会键入的。在引号前弹出 r 并且反斜杠回到普通,便利用于 Windows 路径。
Juno转义序列\n\t\\\" 涵盖日常转义。节省麻烦的那个:原始字符串,r"...",关闭转义处理所以每个反斜杠保持文字。用它用于 Windows 路径和 regex 模式,反斜杠被意思给别的东西而不是 Python。
Juno转义序列 超过 \n\t 有像 \N{name} 的 Unicode 转义,但工作马是原始字符串 r"...",它不变地通过每个反斜杠。在 regex 模式和 Windows 路径上用到它,因为忘记它给你一个无声匹配错误东西的模式而不是告诉你的错误。

检查字符串内容

Python 有方法回答关于字符串包含什么的是-否问题。它们返回 TrueFalse。最早有用的:isdigit() 让你检查一个字符串是否全是数字后转换它,所以你可以避免在意外输入上崩溃。

is* 方法各自测试整个字符串的特定属性并仅当每个字符满足条件时返回 True。它们的主要使用是输入验证:在转换前检查以避免在意外输入上崩溃。isdigit()int() 之前是经典模式,在文件和异常章节中涵盖的捕获 ValueError 的替代方案。

这些检查在整个 Unicode 上问"是否每个字符这种类型",而不仅仅是普通 ASCII 字母和数字。这意味着 "2".isdigit() 对上标二以及普通 "2" 都是 True,这可以让意外输入滑过验证检查。当你指严格的数字 09 时,组合 s.isascii() and s.isdigit()isnumeric() 更宽,计算分数和其他数值字符,所以选择最窄匹配你实际接受的检查而不是看起来正确的第一个。

python
"42".isdigit()       # True
"hello".isalpha()    # True
"hello42".isalnum()  # True
"   ".isspace()      # True
"Hello".islower()    # False
"HELLO".isupper()    # True
Juno检查字符串内容is* 方法回答是-否问题并仅当每个字符适合时返回 True。你首先会使用的那个:在 int() 前调用 isdigit() 确保文本真是一个数字,所以奇怪输入不会崩溃你。
Juno检查字符串内容 每个 is* 方法仅当整个字符串通过时是 True,这使它们便利于在转换前验证输入。isdigit()int() 之前是经典保护。记住它在测试整个字符串,所以空字符串对所有这些返回 False
Juno检查字符串内容 这些检查跨越所有 Unicode,而不是普通 ASCII,所以 isdigit() 通过你没打算接受的上标和其他数值字符。当你指数字 09 时,配对它:s.isascii() and s.isdigit()。选择最窄匹配你真实输入的检查而不是第一个看起来接近的。

实践中

剥离空白,规范化大小写,然后拉出你需要的。这个序列处理几乎任何用户提供的文本:

python
raw_input = "  Zhang@Example.COM  "
email = raw_input.strip().lower()   # "zhang@example.com"

at_pos = email.find("@")
username = email[:at_pos]
domain = email[at_pos + 1:]

print(f"User:   {username}")    # "zhang"
print(f"Domain: {domain}")      # "example.com"

从部分构建 URL 并立即验证和解析它:

python
BASE_URL = "https://api.example.com"
version = "v2"
resource = "users"
user_id = 42

url = f"{BASE_URL}/{version}/{resource}/{user_id}"
# "https://api.example.com/v2/users/42"

parts = url.split("://")              # ["https", "api.example.com/v2/users/42"]
protocol = parts[0]                   # "https"
secured = url.startswith("https")
domain = parts[1].split("/")[0]       # "api.example.com"

print(f"Protocol : {protocol}")
print(f"Secure   : {secured}")
print(f"Domain   : {domain}")

使用 find()、切片和 f-字符串对齐解析结构化日志行:

python
log_entry = "[2024-01-15 09:42:11] ERROR: File not found: report.csv"

timestamp = log_entry[1:20]
rest = log_entry[22:]                # "ERROR: File not found: report.csv"
colon_pos = rest.find(":")
level = rest[:colon_pos]             # "ERROR"
message = rest[colon_pos + 2:]       # "File not found: report.csv"

print(f"[{timestamp}] {level:>8}: {message}")
# [2024-01-15 09:42:11]    ERROR: File not found: report.csv

find() 定位边界,切片提取部分,而 >8 格式规范右对齐严重性标签所以当级别名称长度不同时列保持一致。

方法参考

方法作用
.lower() / .upper()转换为全小写 / 全大写
.title() / .capitalize()大写每个单词 / 仅第一个
.strip() / .lstrip() / .rstrip()移除周围空白
.find(sub)第一个匹配的索引,或 -1
.count(sub)sub 出现的次数
.startswith(s) / .endswith(s)前缀 / 后缀检查
.replace(old, new)替换出现
.split(sep)分割为列表
sep.join(iterable)连接项成字符串
.isdigit() / .isalpha() / .isalnum()字符类型检查