欢迎光临
我们一直在努力

Python3-数据类型-字符串(string)+常见面试题

一、字符串基础特性与定义

1. 定义方式

定义形式语法示例适用场景
单引号 str1 = 'Hello World' 常规场景,内容含双引号时优先
双引号 str2 = "I'm a Python developer" 内容含单引号时优先
三单引号 str3 = '''多行字符串\\n保留格式''' 多行文本、保留换行 / 制表符,非文档注释场景
三双引号 str4 = """文档注释/多行文本""" 多行文本、Python 文档注释(函数 / 类说明)
空字符串 str5 = '' 初始化、判断边界,'' in 任意字符串恒 True

2. 核心特性

特性说明

不可变性

单个字符无法直接修改(如s[0] = 'h'报错),修改操作生成新字符串

有序序列

字符有明确索引,支持索引、切片操作

可哈希性

可作为字典键、集合元素(不可变导致哈希值稳定)

线程安全性

不可变特性无需加锁,多线程操作无并发问题

驻留机制

符合[a-zA-Z0-9_]且长度≤20 的字符串自动缓存,sys.intern()可强制驻留

  • 自动驻留:符合[a-zA-Z0-9_]、长度≤20 的字符串(节省内存)

  • 强制驻留:sys.intern(字符串)(大量重复字符串时使用)

  • 注意:is判断内存地址,==判断内容(永远用==判断相等)

3.示例代码

# -*- coding: utf-8 -*-
"""
Python 字符串基础特性与定义示例代码
涵盖:4种定义方式、不可变性、有序序列、可哈希性、线程安全、驻留机制
"""

import sys
import time
import threading

print("=" * 70)
print("1. 字符串的四种定义方式")
print("=" * 70)

# 1.1 单引号:常规场景,内容含双引号时优先
str1 = 'Hello "Python" World'
print(f"单引号 (含双引号): {str1}")

# 1.2 双引号:内容含单引号时优先
str2 = "I'm a Python developer"
print(f"双引号 (含单引号): {str2}")

# 1.3 三单引号:多行文本,保留换行/制表符 (非文档注释)
str3 = '''第一行
第二行 (保留缩进)
\\t第三行 (制表符)'''
print(f"三单引号 (多行):\\n{str3}")

# 1.4 三双引号:常用于文档注释 (Docstring),也可作多行文本
def demo_func():
"""
这是一个函数文档注释。
使用三双引号包裹。
"""
pass
print(f"函数文档注释: {demo_func.__doc__}")

# 1.5 空字符串:初始化与边界判断
str5 = ''
print(f"\\n空字符串长度: {len(str5)}")
print(f"'' in '任意字符串': {'' in '任意字符串'}") # 恒为 True (空串是任何串的子串)

print("\\n" + "=" * 70)
print("2. 核心特性演示")
print("=" * 70)

# 2.1 不可变性 (Immutability)
# 尝试修改单个字符会报错
s = "Hello"
print(f"\\n原字符串: {s}, ID: {id(s)}")
try:
# s[0] = 'h' # 取消注释会报 TypeError: 'str' object does not support item assignment
pass
except TypeError as e:
print(f"直接修改报错: {e}")

# 修改操作本质是生成新对象
s_new = "h" + s[1:]
print(f"修改后新字符串: {s_new}, ID: {id(s_new)}")
print(f"地址是否改变: {id(s) != id(s_new)}") # True

# 2.2 有序序列 (Ordered Sequence)
# 支持索引和切片
text = "Python Programming"
print(f"\\n原字符串: {text}")
print(f"索引 [0]: {text[0]}") # 'P'
print(f"索引 [-1]: {text[-1]}") # 'g'
print(f"切片 [0:6]: {text[0:6]}") # 'Python'
print(f"切片 [::-1] (反转): {text[::-1]}")

# 2.3 可哈希性 (Hashable)
# 可作为字典键或集合元素
print("\\n[可哈希性测试]")
num_dict = {
"name": "Alice",
100: "Score",
(1, 2): "Tuple Key" # 元组也是可哈希的
}
# 字符串作为键
user_info = {"username": "admin", "role": "root"}
print(f"字典 (字符串作键): {user_info}")

# 字符串放入集合 (自动去重)
char_set = {"a", "b", "a", "c"}
print(f"集合 (自动去重): {char_set}")

# 验证哈希值稳定性
s_hash = "constant"
h1 = hash(s_hash)
h2 = hash(s_hash)
print(f"哈希值稳定性: {h1 == h2}") # True

# 2.4 线程安全性 (Thread Safety)
# 由于不可变,多线程读取无需加锁
print("\\n[线程安全测试]")
shared_str = "Immutable Data"
results = []

def read_data(thread_id):
# 模拟多次读取
for _ in range(1000):
# 即使多线程同时访问,也不会出现数据竞争或脏读
results.append(f"Thread-{thread_id}: {shared_str}")

threads = []
for i in range(5):
t = threading.Thread(target=read_data, args=(i,))
threads.append(t)
t.start()

for t in threads:
t.join()

print(f"多线程读取完成,总记录数: {len(results)} (无并发错误)")

# 2.5 字符串驻留机制 (String Interning)
# Python 会自动缓存符合特定规则的字符串 (字母/数字/下划线, 较短)
print("\\n[驻留机制测试]")

# 情况 A: 符合驻留规则 (字母数字下划线,通常长度<=20)
a = "hello_world_123"
b = "hello_world_123"
print(f"符合规则 ('hello_world_123'): a is b -> {a is b}") # True (共用地址)

# 情况 B: 不符合规则 (包含特殊符号如空格、连字符等)
c = "hello world" # 包含空格
d = "hello world"
# 注意:在脚本文件中,解释器优化可能使 c is d 为 True,但在交互式环境或动态创建时为 False
# 为了演示差异,我们动态创建
c_dyn = "".join(["hello", " ", "world"])
d_dyn = "".join(["hello", " ", "world"])
print(f"含空格 (动态创建): c_dyn is d_dyn -> {c_dyn is d_dyn}") # 通常 False

# 情况 C: 强制驻留 (sys.intern)
e = "special-string!"
f = "special-string!"
print(f"含特殊符号 (未强制): e is f -> {e is f}") # 通常 False

e_interned = sys.intern(e)
f_interned = sys.intern(f)
print(f"含特殊符号 (强制 intern): e_interned is f_interned -> {e_interned is f_interned}") # True

print("\\n演示结束。")

二、字符串索引与切片

1. 索引规则

索引方向起始值示例(s = 'Python')说明
正向索引 0 s[0] → 'P' 从左到右,越界抛IndexError
反向索引 -1 s[-1] → 'n' 从右到左,s[-len(s)] = s[0]

2. 切片语法与特性

基础语法

字符串[起始索引:结束索引:步长]
# 起始索引:包含(默认0);结束索引:不包含(默认len(字符串));步长:默认1(负数反向)

核心特性与示例

用法示例(s = 'HelloPython')说明
基础切片 s[0:5] → 'Hello' 取索引 0-4 的字符(左闭右开)
省略起始索引 s[:5] → 'Hello' 从开头到结束索引
省略结束索引 s[5:] → 'Python' 从起始索引到末尾
反向切片(反转) s[::-1] → 'nohtyPolleH' 最优字符串反转方案
指定步长 s[0:10:2] → 'HloPto' 每隔 1 个字符取 1 个
惰性复制 s_slice = s[:5] 切片仅记录原字符串引用 + 索引,修改时才复制数据

3.示例代码

# -*- coding: utf-8 -*-
"""
Python 字符串索引与切片示例代码
涵盖:正向/反向索引、切片基础语法、步长控制、反转技巧、惰性复制原理
"""

print("=" * 70)
print("1. 字符串索引规则 (Indexing)")
print("=" * 70)

s = "Python"
print(f"原字符串: '{s}' (长度: {len(s)})")

# 1.1 正向索引 (0 开始,从左到右)
print("\\n[正向索引]")
print(f"s[0] : '{s[0]}' (第一个字符)")
print(f"s[2] : '{s[2]}' (第三个字符)")
print(f"s[5] : '{s[5]}' (最后一个字符)")

# 越界测试
try:
# print(s[6]) # 取消注释会报 IndexError
pass
except IndexError as e:
print(f"正向越界报错: {e}")

# 1.2 反向索引 (-1 开始,从右到左)
print("\\n[反向索引]")
print(f"s[-1] : '{s[-1]}' (最后一个字符,等价于 s[5])")
print(f"s[-2] : '{s[-2]}' (倒数第二个字符)")
print(f"s[-6] : '{s[-6]}' (第一个字符,等价于 s[0])")

# 反向越界测试
try:
# print(s[-7]) # 取消注释会报 IndexError
pass
except IndexError as e:
print(f"反向越界报错: {e}")

print("\\n" + "=" * 70)
print("2. 切片基础语法 (Slicing)")
print("=" * 70)
# 语法: string[start:end:step]
# 规则: [start, end) 左闭右开,包含 start 不包含 end

text = "HelloPython"
print(f"原字符串: '{text}' (长度: {len(text)})")
# 索引对应:
# H e l l o P y t h o n
# 0 1 2 3 4 5 6 7 8 9 10
# -11… -1

# 2.1 基础切片 [start:end]
print("\\n[基础切片]")
sub1 = text[0:5]
print(f"text[0:5] : '{sub1}' (取索引 0 到 4,'Hello')")

# 2.2 省略起始索引 [:end] -> 默认从 0 开始
print("\\n[省略起始索引]")
sub2 = text[:5]
print(f"text[:5] : '{sub2}' (从开头到索引 4)")

# 2.3 省略结束索引 [start:] -> 默认到末尾
print("\\n[省略结束索引]")
sub3 = text[5:]
print(f"text[5:] : '{sub3}' (从索引 5 到末尾,'Python')")

# 2.4 省略起止 [:] -> 浅拷贝整个字符串
sub4 = text[:]
print(f"text[:] : '{sub4}' (完整拷贝,但地址可能因驻留机制相同)")
print(f"地址比较: {id(text) == id(sub4)}")

print("\\n" + "=" * 70)
print("3. 步长与高级切片 (Step & Advanced)")
print("=" * 70)

# 3.1 指定步长 [start:end:step]
print("\\n[指定步长]")
# 每隔 1 个字符取 1 个 (即取索引 0, 2, 4, 6, 8…)
sub_step = text[0:10:2]
print(f"text[0:10:2]: '{sub_step}' ('HloPto')")

# 3.2 反向切片 (字符串反转神器)
print("\\n[反向切片/反转]")
reversed_s = text[::-1]
print(f"text[::-1] : '{reversed_s}' ('nohtyPolleH')")
print("说明: step 为 -1 时,默认 start 为末尾,end 为开头")

# 3.3 负数步长配合索引
# 取倒数第 5 个到倒数第 2 个,并反转
# 原串: H(0)…o(-5) P(-6)…n(-1)
# 目标范围: oPyt (索引 4 到 7) -> 反转后: tyPo
sub_neg = text[-2:-6:-1]
# 解析: start=-2('o'), end=-6('P', 不包含), step=-1
# 路径: -2('o') -> -3('t') -> -4('y') -> -5('P') -> 停止 (因为 -6 不包含)
print(f"text[-2:-6:-1]: '{sub_neg}' ('otyP' ? 稍等,让我们仔细推导)")
# 修正推导:
# text = "HelloPython"
# 索引: 01234567890
# 负号: -11-10-9-8-7-6-5-4-3-2-1
# 字符: H e l l o P y t h o n
# text[-2] 是 'o' (倒数第 2 个)
# text[-6] 是 'P' (倒数第 6 个)
# text[-2:-6:-1] -> 从 'o' 开始,向左走,直到 'P' (不含)
# 序列: 'o' (idx -2), 'h' (idx -3), 't' (idx -4), 'y' (idx -5). 下一个是 'P' (idx -6, 停止)
# 结果应该是 'ohty'
print(f"实际输出: '{sub_neg}' (推导验证: o->h->t->y)")

print("\\n" + "=" * 70)
print("4. 惰性复制与内存优化 (Lazy Copy)")
print("=" * 70)
# Python 的切片操作通常是“浅拷贝”对于不可变对象
# 对于字符串,切片对象在内部可能只是记录原字符串的引用 + 偏移量 + 长度
# 只有当需要修改(生成新串)或特定实现细节下才会真正复制数据

large_str = "A" * 1000000 # 100 万个字符
print(f"原字符串长度: {len(large_str)}, 占用内存: {len(large_str)} bytes (近似)")

# 执行切片
slice_str = large_str[100:200]

# 验证内容
print(f"切片后长度: {len(slice_str)}")
print(f"切片内容前 10 位: '{slice_str[:10]}'")

# 内存说明:
# 在 CPython 实现中,字符串切片通常会创建一个新的字符串对象并复制数据。
# 但概念上,它被视为一种高效的序列视图操作。
# 真正的“惰性/零拷贝”在 Python 字符串中不如在 numpy 或 memoryview 中明显,
# 但对于不可变对象,解释器会尽可能优化内存管理(如共享底层缓冲区在某些版本/场景中)。
# 重点:切片不会修改原字符串,而是返回新字符串。

import sys
original_size = sys.getsizeof(large_str)
slice_size = sys.getsizeof(slice_str)
print(f"\\n原对象内存: {original_size} bytes")
print(f"切片对象内存: {slice_size} bytes (仅包含切片部分的实际数据)")
print("结论: 切片生成了独立的新对象,只存储截取部分的数据,未保留对大字符串的引用防止内存泄漏。")

print("\\n演示结束。")

三、字符串基本操作

1. 拼接与重复

操作语法示例性能说明
拼接 + 'Hello' + 'Python' 小规模可用,大规模频繁创建新对象(低效)
重复 * 'Hello' * 3 生成重复字符串,'a'*0返回空字符串
高效拼接 ''.join() ''.join(['a','b','c']) 时间复杂度 O (n),循环拼接首选
超大拼接 io.StringIO 见下方示例 避免频繁创建对象,超大量字符串拼接最优

# 超大字符串拼接示例
import io
buffer = io.StringIO()
for i in range(100000):
buffer.write(str(i))
result = buffer.getvalue() # 最终拼接结果

2. 成员判断与长度计算

操作语法示例
成员存在 in 'Python' in 'Python is fun'
成员不存在 not in 'Java' not in 'Python is fun'
长度计算 len() len('中文') → 2(Unicode 字符数)

3.示例代码

# -*- coding: utf-8 -*-
"""
Python 字符串基本操作示例代码
涵盖:拼接与重复(+、*、join、StringIO)、成员判断、长度计算
"""

import io
import time

print("=" * 70)
print("1. 字符串拼接与重复 (Concatenation & Repetition)")
print("=" * 70)

# 1.1 使用 + 号拼接 (小规模场景)
# 适用:少量字符串拼接,代码可读性高
s1 = "Hello"
s2 = "Python"
result_plus = s1 + " " + s2
print(f"[+] 拼接: '{result_plus}'")

# 性能警告:在循环中频繁使用 + 会创建大量临时对象,效率极低
# 每次 + 操作都会分配新内存并复制旧数据,时间复杂度接近 O(n^2)

# 1.2 使用 * 号重复
# 适用:生成重复图案、占位符
repeat_str = "Ha" * 3
print(f"[*] 重复: '{repeat_str}'") # HaHaHa

zero_repeat = "Test" * 0
print(f"[*] 零次重复: '{zero_repeat}' (空字符串)") # ''

# 1.3 使用 ''.join() (推荐:循环拼接的标准做法)
# 适用:列表/元组转字符串,循环内拼接
# 时间复杂度 O(n),只分配一次内存
char_list = ['P', 'y', 't', 'h', 'o', 'n']
result_join = "".join(char_list)
print(f"[join] 列表转字符串: '{result_join}'")

# 模拟循环拼接对比
words = ["Line"] * 1000

# ❌ 低效写法 (注释掉以免运行慢,仅示意)
# bad_result = ""
# for w in words:
# bad_result += w + "\\n"

# ✅ 高效写法
good_result = "\\n".join(words)
print(f"[join] 高效拼接 1000 行: 总长度 {len(good_result)} 字符")

# 1.4 使用 io.StringIO (超大规模拼接的最优解)
# 适用:海量数据流、日志缓冲、避免内存碎片
# 原理:内部维护一个可变缓冲区,类似文件写入,最后一次性导出
print("\\n[StringIO] 超大规模拼接测试…")
start_time = time.time()

buffer = io.StringIO()
count = 100000
for i in range(count):
buffer.write(f"ID:{i},Data:{i*2};") # 模拟写入结构化数据

huge_result = buffer.getvalue()
end_time = time.time()

print(f"拼接数量: {count} 条")
print(f"结果总长度: {len(huge_result)} 字符")
print(f"耗时: {(end_time – start_time)*1000:.2f} ms")
print("说明: StringIO 避免了每次拼接都创建新字符串对象,内存效率最高。")

print("\\n" + "=" * 70)
print("2. 成员判断 (Membership Testing)")
print("=" * 70)

text = "Python is fun and powerful"

# 2.1 in (存在性判断)
is_present = "Python" in text
print(f"'Python' in text: {is_present}") # True

is_sub = "fun" in text
print(f"'fun' in text: {is_sub}") # True

# 2.2 not in (不存在性判断)
is_absent = "Java" not in text
print(f"'Java' not in text: {is_absent}") # True

# 区分大小写
case_check = "python" in text
print(f"'python' (小写) in text: {case_check}") # False (区分大小写)

print("\\n" + "=" * 70)
print("3. 长度计算 (Length Calculation)")
print("=" * 70)

# 3.1 英文字符串
eng_str = "Hello"
print(f"len('{eng_str}'): {len(eng_str)}") # 5

# 3.2 中文字符串 (重点:Unicode 字符数)
# Python 3 中 len() 统计的是 Unicode 码点数量,而非字节数
zh_str = "中文"
print(f"len('{zh_str}'): {len(zh_str)}") # 2 (不是 4 或 6)

# 3.3 混合字符串
mixed_str = "Hi 你好 🚀"
print(f"len('{mixed_str}'): {len(mixed_str)}")
# 解析: H(1) + i(1) + 空格 (1) + 你 (1) + 好 (1) + 空格 (1) + 🚀(1) = 7
# 注意:Emoji 表情在 UTF-8 编码下可能占 4 个字节,但 len() 仍计为 1 个字符

# 对比:字节长度 (encode 后)
byte_len = zh_str.encode('utf-8')
print(f"'{zh_str}' 的 UTF-8 字节长度: {len(byte_len)}") # 通常中文为 3 字节/字,2*3=6

print("\\n演示结束。")

四、字符串转义与原始字符串

1. 常用转义字符

转义符含义示例
\\n 换行 print('hello\\nworld')
\\t 制表符 print('hello\\tworld')
\\' 单引号 print('\\'hello\\'')
\\" 双引号 print("\\"hello\\"")
\\\\ 反斜杠 print('C:\\\\Python')
\\r 回车 print('hello\\rworld') → world
\\u200b 零宽空格 肉眼不可见,易导致匹配异常

2. 原始字符串

  • 语法:前缀r/R,关闭转义功能

  • 场景:文件路径、正则表达式编写

  • 示例:r'C:\\Python\\test'(\\t不被解析为制表符)

3.示例代码

# -*- coding: utf-8 -*-
"""
Python 字符串转义与原始字符串示例代码
涵盖:常用转义字符、特殊控制符、原始字符串 (Raw String) 的应用场景
"""

print("=" * 70)
print("1. 常用转义字符 (Escape Characters)")
print("=" * 70)

# 1.1 换行 (\\n) 与 制表符 (\\t)
text_basic = "Line1\\nLine2\\t(Tabbed)"
print("[\\\\n & \\\\t] 输出效果:")
print(text_basic)
# 实际存储: 'Line1\\nLine2\\t(Tabbed)'

# 1.2 引号转义 (\\' 与 \\")
# 场景:在单引号字符串中包含单引号,或双引号中包含双引号
quote_single = 'It\\'s a sunny day'
quote_double = "He said \\"Hello\\""
print(f"\\n[\\\\'] 单引号转义: {quote_single}")
print(f"[\\\\\\"] 双引号转义: {quote_double}")

# 1.3 反斜杠本身 (\\\\)
# 场景:Windows 文件路径 (传统写法)
path_legacy = "C:\\\\Users\\\\Admin\\\\Documents"
print(f"\\n[\\\\\\\\] 反斜杠转义: {path_legacy}")
# 注意:打印出来是 C:\\Users\\Admin\\Documents,但内存中存的是两个字符 '\\'

# 1.4 回车符 (\\r) – 覆盖效果
# \\r 将光标移回行首,后续字符会覆盖前面的字符
# 'hello\\rworld' -> 先打印 hello, 光标回开头, 打印 world (覆盖 hell) -> 显示 world
cr_text = "hello\\rworld"
print(f"\\n[\\\\r] 回车覆盖测试: '{cr_text}'")
# 终端显示应为: world
# 解释: h e l l o -> 光标回退 -> w o r l d (覆盖了 h e l l, o 被 w 覆盖? 不,w 覆盖 h, o 覆盖 e, r 覆盖 l, l 覆盖 l, d 覆盖 o)
# 详细推导:
# 初始: h e l l o
# \\r: 光标回到 h 前面
# 写入 w: w e l l o
# 写入 o: w o l l o
# 写入 r: w o r l o
# 写入 l: w o r l l
# 写入 d: w o r l d
# 最终显示: world

# 1.5 零宽空格 (\\u200b) – 隐形坑
# 肉眼不可见,但会影响字符串长度和匹配
zero_width = "Hello\\u200bWorld"
print(f"\\n[\\\\u200b] 零宽空格测试:")
print(f"显示内容: '{zero_width}'") # 看起来像 HelloWorld
print(f"实际长度: {len(zero_width)}") # 11 (Hello=5 + World=5 + 零宽=1)
print(f"包含零宽空格: {'\\u200b' in zero_width}") # True

print("\\n" + "=" * 70)
print("2. 原始字符串 (Raw Strings)")
print("=" * 70)
# 语法: 前缀 r 或 R
# 作用: 关闭转义机制,反斜杠被视为普通字符

# 2.1 路径处理 (最典型场景)
path_raw = r"C:\\Users\\NewFolder\\test.txt"
print(f"[r''] Windows 路径: {path_raw}")
# 对比普通字符串: "C:\\Users\\NewFolder\\test.txt"
# 其中的 \\n 会被解析为换行,\\t 被解析为制表符,导致路径错误

# 验证 \\t 是否被转义
raw_tab = r"Tab:\\tHere"
norm_tab = "Tab:\\tHere"
print(f"\\n原始字符串 (\\\\t 不转义): '{raw_tab}'") # 输出: Tab:\\tHere
print(f"普通字符串 (\\\\t 转义): '{norm_tab}'") # 输出: Tab: Here (带制表符)

# 2.2 正则表达式 (Regex)
# 正则中大量使用 \\d, \\w, \\s, \\n 等,使用 raw string 可避免双重转义
import re

text = "My phone is 123-4567"
# ❌ 普通写法: 需要写四个反斜杠才能匹配一个数字 (\\d -> \\\\d -> "\\\\\\\\d")
# pattern_bad = "\\\\d+-\\\\d+"

# ✅ 推荐写法: r"\\d+-\\d+" (只需两个反斜杠,直观易读)
pattern_raw = r"\\d+-\\d+"
match = re.search(pattern_raw, text)
print(f"\\n[Regex] 匹配结果: {match.group() if match else 'No Match'}")

# 2.3 原始字符串的限制
# 注意:原始字符串不能以奇数个反斜杠结尾,因为会转义后面的引号
try:
# invalid_path = r"C:\\Path\\" # SyntaxError: EOL while scanning string literal
pass
except SyntaxError:
print("\\n[警告] 原始字符串不能以单个反斜杠结尾 (会转义结束引号)")

# 解决方法:切片掉最后一个字符,或者拼接一个普通空串
valid_path = r"C:\\Path" + "\\\\"
print(f"[修正] 正确结尾方式: {valid_path}")

print("\\n" + "=" * 70)
print("3. 综合对比总结")
print("=" * 70)
s_normal = "Line1\\nLine2"
s_raw = r"Line1\\nLine2"

print(f"普通字符串: {repr(s_normal)} -> 打印:\\n{s_normal}")
print(f"原始字符串: {repr(s_raw)} -> 打印:\\n{s_raw}")
print("\\n结论: 涉及路径、正则、包含大量反斜杠的文本时,优先使用 r'' 原始字符串。")

print("\\n演示结束。")

五、字符串格式化(3 种方式)

方式语法示例特点
% 格式化(旧式) 'Hello %s, %d岁' % ('张三', 20) 兼容旧代码,需匹配格式符类型
str.format ()(新式) 'Hello {}, {}岁'.format('张三', 20) 支持位置 / 关键字参数,更灵活
f-string(推荐) f'Hello {name}, {age}岁' Python3.6+,简洁高效,支持表达式
f-string 增强(3.8+) f'{x=}' → x=10 直接显示变量名 + 值,调试实用
Template(安全) Template('姓名:$name').substitute(name='张三') 避免注入风险,适合动态替换用户输入

# -*- coding: utf-8 -*-
"""
Python 字符串格式化示例代码
涵盖:% 格式化、str.format()、f-string (含调试模式)、string.Template
"""

from string import Template

print("=" * 70)
print("1. % 格式化 (旧式,Legacy)")
print("=" * 70)
# 特点:类似 C 语言 printf,需严格匹配类型,兼容旧代码

name = "张三"
age = 20
height = 1.756

# %s: 字符串, %d: 整数, %f: 浮点数
msg_percent = "姓名:%s, 年龄:%d岁, 身高:%.2f米" % (name, age, height)
print(f"[%%] 基础用法: {msg_percent}")

# 字典映射方式 (当参数较多时较清晰)
data = {"name": "李四", "age": 25}
msg_dict = "姓名:%(name)s, 年龄:%(age)d岁" % data
print(f"[%%] 字典映射: {msg_dict}")

# 注意:类型不匹配会报错
# "%d" % "20" # TypeError

print("\\n" + "=" * 70)
print("2. str.format() (新式,灵活)")
print("=" * 70)
# 特点:支持位置参数、关键字参数、属性访问、复杂格式控制

# 2.1 位置参数
msg_pos = "Hello {}, {}岁".format("王五", 30)
print(f"[.format] 位置参数: {msg_pos}")

# 2.2 关键字参数 (推荐,可读性高)
msg_kw = "姓名:{name}, 年龄:{age}".format(name="赵六", age=28)
print(f"[.format] 关键字: {msg_kw}")

# 2.3 重复使用参数
msg_repeat = "{0} 喜欢 {1}, {0} 经常玩 {1}".format("小明", "Python")
print(f"[.format] 重复引用: {msg_repeat}")

# 2.4 格式控制 (对齐、精度、进制)
num = 123.45678
pi = 3.1415926
bin_num = 10

# :.2f -> 保留 2 位小数
# :<10 -> 左对齐,总宽 10
# :^10 -> 居中对齐
# :b -> 二进制
msg_fmt = "数字:{:.2f}, 左对齐:[{: <10}], 二进制:{:b}".format(num, num, bin_num)
print(f"[.format] 格式控制: {msg_fmt}")

# 2.5 访问对象属性/索引
class Person:
def __init__(self, name):
self.name = name

p = Person("孙七")
msg_obj = "对象属性:{p.name}".format(p=p)
lst = ["A", "B", "C"]
msg_idx = "列表索引:{l[0]}".format(l=lst)
print(f"[.format] 属性/索引: {msg_obj}, {msg_idx}")

print("\\n" + "=" * 70)
print("3. f-string (Python 3.6+, 强烈推荐)")
print("=" * 70)
# 特点:语法最简洁,性能最高,支持直接在花括号内写表达式

user = "周八"
score = 95.5
level = "A"

# 3.1 基础变量插入
msg_f = f"用户:{user}, 得分:{score}"
print(f"[f-str] 基础: {msg_f}")

# 3.2 支持表达式计算
msg_expr = f"明年分数预测:{score + 2}, 是否优秀:{score >= 90}"
print(f"[f-str] 表达式: {msg_expr}")

# 3.3 格式控制 (同 format 语法,但更紧凑)
msg_align = f"姓名:{user:<10}, 分数:{score:.1f}"
print(f"[f-str] 格式控制: '{msg_align}'")

# 3.4 调用函数
msg_func = f"大写名字:{user.upper()}"
print(f"[f-str] 函数调用: {msg_func}")

# 3.5 调试模式 (Python 3.8+)
# 语法:{variable=} 自动输出 "variable=value"
x = 100
y = 200
msg_debug = f"调试信息:{x=}, {y=}, {x+y=}"
print(f"[f-str] 调试模式 (3.8+): {msg_debug}")
# 输出示例:调试信息:x=100, y=200, x+y=300

print("\\n" + "=" * 70)
print("4. string.Template (安全替换)")
print("=" * 70)
# 特点:语法简单 ($var),不进行 Python 代码执行,适合用户输入模板,防注入

template_str = "欢迎 ${name} 加入 ${group} 群!"
tmpl = Template(template_str)

# 安全替换
safe_msg = tmpl.substitute(name="吴九", group="Python 学习")
print(f"[Template] 安全替换: {safe_msg}")

# 处理包含 $ 符号的文本 (转义 $$)
price_template = Template("价格:$$${price} 元") # $$ 转义为单个 $
price_msg = price_template.substitute(price="99")
print(f"[Template] 转义$: {price_msg}")

# 为什么安全?
# 如果用户输入 name = "__import__('os').system('rm -rf /')"
# Template 只会将其作为纯文本替换,不会执行代码
# 而 f-string 或 eval 可能会有风险(如果直接拼接用户代码)
dangerous_input = "${__import__('os').system('echo hacked')}"
# 即使放入 Template,它也只会被当作普通字符串处理,不会执行
tmpl_safe = Template("输入内容:$input")
# 注意:substitute 需要关键字参数名匹配,这里演示如果用字典
result_safe = tmpl_safe.safe_substitute(input=dangerous_input)
print(f"[Template] 防注入演示: {result_safe}")
# 输出:输入内容:${__import__('os').system('echo hacked')} (原样输出,未执行)

print("\\n" + "=" * 70)
print("5. 性能与场景总结")
print("=" * 70)
print("""
场景推荐:
1. 日常开发、日志记录、API 响应构建 -> 首选 f-string (最快、最易读)
2. 需要兼容 Python 3.5 及以下 -> 使用 str.format()
3. 维护古老代码库 -> 可能会遇到 % 格式化
4. 处理用户提供的模板字符串 (如邮件模板、配置文件) -> 使用 string.Template (安全)
""")

print("\\n演示结束。")

六、字符串内置方法(分类汇总)

1. 大小写处理

方法作用示例
upper() 转大写 'hello'.upper() → 'HELLO'
lower() 转小写 'HELLO'.lower() → 'hello'
capitalize() 首字母大写,其余小写 'hello python' → 'Hello python'
title() 每个单词首字母大写 'hello python' → 'Hello Python'
swapcase() 大小写互换 'Hello' → 'hELLO'
casefold() 增强版小写(支持多语言,如德语 ß) 'Straße'.casefold() → 'strasse'

2. 查找与替换

方法作用示例
find(sub) 查找子串位置,找不到返回 – 1 'hello'.find('l') → 2
rfind(sub) 从右侧查找子串 'hello'.rfind('l') → 3
index(sub) 同 find,但找不到抛异常 'hello'.index('x') → 报错
rindex(sub) 从右侧查找,找不到抛异常
replace(old, new, count) 替换子串(count 指定次数) 'hello'.replace('l', 'x', 1) → 'hexlo'
count(sub) 统计子串出现次数 'hello ll'.count('l') → 3

3. 空白 / 字符去除

方法作用示例
strip([chars]) 去除首尾空白 / 指定字符 '###hello###'.strip('#') → 'hello'
lstrip([chars]) 去除左侧空白 / 指定字符 ' hello '.lstrip() → 'hello '
rstrip([chars]) 去除右侧空白 / 指定字符 ' hello '.rstrip() → ' hello'
removeprefix(prefix) 去除前缀(3.10+,语义化) 'HelloPython'.removeprefix('Hello') → 'Python'
removesuffix(suffix) 去除后缀(3.10+,语义化) 'HelloPython'.removesuffix('Python') → 'Hello'

4. 分割与拼接

方法作用示例
split(sep, maxsplit) 按分隔符分割(maxsplit 指定次数) 'a,b,c'.split(',') → ['a','b','c']
rsplit(sep) 从右侧分割 'a,b,c'.rsplit(',', 1) → ['a,b','c']
splitlines([keepends]) 按换行符分割,keepends 保留换行符 'a\\nb\\nc'.splitlines() → ['a','b','c']
join(iterable) 拼接可迭代对象为字符串(高效) ','.join(['a','b','c']) → 'a,b,c'
partition(sep) 分割为 (前,分隔符,后) 三元组 'a,b,c'.partition(',') → ('a', ',', 'b,c')
rpartition(sep) 从右侧分割为三元组 'a,b,c'.rpartition(',') → ('a,b', ',', 'c')

5. 判断类方法(返回布尔值)

方法作用注意点
isalpha() 是否全为字母 / 中文 'Python编程' → True
isdigit() 是否全为数字(含①、² 等数字字符) '123'→True,'①'→True
isdecimal() 是否全为十进制数字(最严格) '123'→True,'①'→False
isnumeric() 是否全为数字字符(含汉字 / 罗马数字) '壹'→True,'Ⅲ'→True
isalnum() 是否全为字母 / 数字 'Python123'→True
isspace() 是否全为空白字符(空格 / 制表符 / 换行) ' \\t\\n'→True
islower()/isupper() 是否全小写 / 全大写 需包含至少一个区分大小写字符
istitle() 是否为标题格式(每个单词首字母大写) 'Hello Python'→True
startswith(prefix) 是否以指定前缀开头 'Python'.startswith('Py')→True
endswith(suffix) 是否以指定后缀结尾 'Python'.endswith('on')→True
isidentifier() 是否为合法 Python 标识符(变量名) 'python_123'→True,'123python'→False

6. 格式化与对齐

方法作用示例
center(width, fillchar) 居中对齐,填充指定字符 'hello'.center(10, '*') → '**hello***'
ljust(width, fillchar) 左对齐 'hello'.ljust(10, '*') → 'hello*****'
rjust(width, fillchar) 右对齐 'hello'.rjust(10, '*') → '*****hello'
zfill(width) 补零(数字字符串,右对齐) '123'.zfill(5) → '00123'
expandtabs(tabsize) 替换 \\t 为指定空格数(默认 8) 'a\\tb'.expandtabs(4) → 'a b'

7. 编码与解码

方法作用示例
encode(encoding='utf-8', errors='strict') 字符串→字节串 '中文'.encode('utf-8') → b'\\xe4\\xb8\\xad\\xe6\\x96\\x87'
decode(encoding='utf-8', errors='strict') 字节串→字符串(需匹配编码) b'\\xe4\\xb8\\xad'.decode('utf-8') → '中'
  • 核心规则:Python3 字符串默认 Unicode 编码,编码 / 解码优先用utf-8

  • 常见错误:UnicodeDecodeError(编码格式不匹配)

  • 解决方案:

    b = b'Python \\xe7\\xbc\\x96\\xe7\\xa8\\x8b\\x80'
    b.decode('utf-8', errors='ignore') # 忽略错误字符
    b.decode('utf-8', errors='replace') # 错误字符替换为�

  • BOM 处理:utf-8-sig编码自动添加 / 去除字节顺序标记

8. 其他实用方法

方法作用示例
maketrans()+translate() 批量字符映射替换 见下方示例
max()/min() 返回字符串中最大 / 最小字母 max('Python') → 'y'
ascii() 仅输出 ASCII 字符,非 ASCII 转义 ascii('Python编程') → 'Python \\u7f16\\u7a0b'
ord()/chr() 字符↔Unicode 编码互转 ord('中')→20013,chr(20013)→'中'

# maketrans+translate批量替换示例
trans_table = str.maketrans({'a':'1', 'b':'2', 'c':'3'}, 'd') # a→1,b→2,c→3,删除d
s = 'abcde123'
print(s.translate(trans_table)) # 输出:123e123

七、字符串与其他数据类型交互

1. 字符串↔数字

转换方向方法示例
数字→字符串 str()/f-string str(123) → '123'
字符串→数字 int()/float() int('123') → 123(需处理 ValueError)

2. 字符串↔列表 / 元组

转换方向方法示例
字符串→列表 list() list('hello') → ['h','e','l','l','o']
列表→字符串 ''.join() ''.join(['h','e','l','l','o']) → 'hello'
字符串→元组 tuple() tuple('hello') → ('h','e','l','l','o')

3. 字符串↔字节串(bytes)

转换方向方法示例
字符串→字节串 encode() '中文'.encode('utf-8')
字节串→字符串 decode() b'\\xe4\\xb8\\xad'.decode('utf-8')

!!!核心常见面试题

1. 问题:Python 字符串为什么是不可变的?不可变性带来了哪些好处?

陷阱分析

新手常误以为 “不可变” 是 “不能操作”(比如拼接、切片),实则混淆 “对象本身修改” 和 “创建新对象”;或只说表面现象,答不出底层原理。

正确解答
  • 底层本质:Python 字符串底层由PyUnicodeObject结构体实现,其核心是存储字符的固定长度数组(如wchar_t数组),数组的内存地址和长度在创建后不可修改。任何看似 “修改” 字符串的操作(如s.replace()、s + "a"),本质都是创建新的字符串对象,原对象不变。

  • 核心好处:

  • 哈希安全:不可变保证字符串的哈希值固定,可作为字典的 key、集合的元素;

  • 线程安全:多线程操作时无需加锁,避免数据竞争;

  • 内存优化:可通过 “字符串驻留机制” 缓存重复的小字符串,减少内存占用。

2. 问题:is 和 == 判断字符串有什么区别?举例说明坑点。

陷阱分析

新手常混用is和==,误以为 “内容相等的字符串,内存地址一定相等”,忽略字符串驻留机制的规则限制。

正确解答
  • ==:判断内容是否相等,会逐字符比较字符串的值,是判断字符串相等的正确方式;

  • is:判断内存地址是否相等(是否为同一个对象),依赖 Python 的 “字符串驻留机制”,仅对符合规则的字符串生效(如:长度 < 20、仅含字母 / 数字 / 下划线的小字符串)。

  • s1 = "hello"
    s2 = "hello"
    print(s1 == s2) # True(内容相等)
    print(s1 is s2) # True(驻留机制,同一对象)

    s3 = "hello world!" # 含空格,不满足驻留规则
    s4 = "hello world!"
    print(s3 == s4) # True
    print(s3 is s4) # False(不同对象)

    s5 = str(123)
    s6 = "123"
    print(s5 == s6) # True
    print(s5 is s6) # False(动态生成的字符串不驻留)

  • 结论:永远用==判断字符串内容相等,仅在确认需要比较内存地址时用is。

3. 问题:字符串切片 s[a:b:c] 的底层逻辑是什么?修改切片结果会影响原字符串吗?

陷阱分析

新手误以为切片会复制原字符串的所有数据,或认为修改切片结果能改变原字符串。

正确解答
  • 底层逻辑:切片不直接复制原字符串的字符数据,而是创建一个新的字符串对象,该对象仅记录 “原字符串的引用 + 起始索引 + 结束索引 + 步长”。只有当对切片结果进行修改操作(如拼接、替换)时,才会触发 “写时复制(COW)” 机制,真正复制数据生成新对象。

  • 核心结论:切片结果是新对象,但底层复用原字符串数据(只读);由于字符串不可变,修改切片结果(如new_s = s[:2] + "a")不会影响原字符串。示例:

    s = "abcdef"
    s_slice = s[1:4] # 'bcd',仅记录原字符串引用+索引,未复制数据
    s_slice_new = s_slice + "x" # 触发写时复制,生成新字符串 'bcdx'
    print(s) # 原字符串仍为 'abcdef',无变化

4. 问题:循环中拼接大量字符串,s += str(i) 和 ''.join(列表) 哪个效率高?为什么?

陷阱分析

新手习惯用+=拼接字符串,忽略其频繁创建新对象的性能损耗,尤其数据量大时差距极明显。

正确解答
  • s += str(i):效率极低。因为字符串不可变,每次+=都会创建一个新的字符串对象,复制原字符串 + 新字符的所有数据。循环 N 次的时间复杂度为O(N2)(每次复制的字符数递增)。

  • ''.join(列表):效率最优。先将所有待拼接的字符串存入列表(列表是可变类型,追加元素无性能损耗),最后调用join方法一次性拼接。join会先计算所有字符串的总长度,分配一次内存,再批量复制数据,时间复杂度为O(N)。

  • 对比示例:

    import time

    # 低效:+= 拼接
    start = time.time()
    s = ""
    for i in range(100000):
    s += str(i)
    print(f"+= 耗时:{time.time() – start:.2f}秒") # 约0.08秒(数据量越大差距越明显)

    # 高效:join 拼接
    start = time.time()
    lst = []
    for i in range(100000):
    lst.append(str(i))
    s = ''.join(lst)
    print(f"join 耗时:{time.time() – start:.2f}秒") # 约0.01秒

  • 结论:拼接大量字符串必用''.join(列表),禁用循环+=。

5. 问题:处理超大字符串(如 10GB 日志文件)时,如何避免内存溢出?

陷阱分析

新手习惯一次性读取整个文件到字符串(s = open("file.log").read()),导致内存占满崩溃。

正确解答

核心思路:分块读取 + 跨块兼容处理,不加载整个字符串到内存:

# 正确做法:分块读取,每次处理4KB数据
with open("large_file.log", "r", encoding="utf-8") as f:
buffer = "" # 缓存跨块的不完整内容(如一行的后半部分)
while True:
chunk = f.read(4096) # 每次读取4KB
if not chunk:
break # 读取完毕
# 拼接缓存+当前块,按行处理
content = buffer + chunk
lines = content.split("\\n")
# 最后一行可能不完整,存入缓存
buffer = lines.pop()
# 处理完整的行
for line in lines:
process_line(line) # 自定义处理逻辑
# 处理最后一行缓存
if buffer:
process_line(buffer)

  • 核心考点:理解 “字符串分块处理” 的思想,避免一次性加载大字符串到内存。

6. 问题:为什么 'C:\\new.txt' 中的路径会出错?如何正确处理 Windows 路径?

陷阱分析

新手忽略反斜杠\\是转义符,\\n会被解析为换行符,导致路径错误。

正确解答
  • 错误原因:Python 中\\是转义符,\\n→换行、\\t→制表符、\\r→回车等,直接写'C:\\new.txt'会被解析为'C:\\new.txt'(含换行符),路径无效。

  • 正确用法(3 种):

    # 方法1:原始字符串(推荐),前缀r禁用转义
    path = r'C:\\new.txt'
    # 方法2:双反斜杠转义
    path = 'C:\\\\new.txt'
    # 方法3:使用os.path或pathlib(跨平台最优)
    import os
    path = os.path.join("C:", "new.txt")

7. 问题:'年龄:' + 20 会报错,如何正确拼接字符串和数字?

陷阱分析

新手忽略 Python 无 “隐式类型转换”,字符串和数字直接拼接会触发TypeError。

正确解答
  • 错误原因:Python 不支持不同类型的隐式拼接,str + int 会报错 TypeError: can only concatenate str (not "int") to str。

  • 正确用法(4 种,推荐 f-string):

    # 方法1:f-string(Python3.6+,简洁高效)
    res = f'年龄:{20}'
    # 方法2:str() 转换
    res = '年龄:' + str(20)
    # 方法3:format() 格式化
    res = '年龄:{}'.format(20)
    # 方法4:% 格式化(旧写法,不推荐)
    res = '年龄:%d' % 20

8. 问题:正则匹配 <div>aaa</div><div>bbb</div> 时,r'<div>(.*)</div>' 为什么只匹配到最后一个</div>?如何修复?

陷阱分析

新手忽略正则默认是 “贪婪匹配”,会尽可能匹配更多内容。

正确解答
  • 错误原因:.* 是贪婪匹配,会从第一个<div>开始,一直匹配到最后一个</div>,导致结果包含所有中间内容。

  • 修复方法:使用.*? 非贪婪匹配,匹配到第一个</div>就停止:

    import re

    s = '<div>aaa</div><div>bbb</div>'
    # 贪婪匹配(错误)
    res1 = re.search(r'<div>(.*)</div>', s)
    print(res1.group(1)) # aaa</div><div>bbb

    # 非贪婪匹配(正确)
    res2 = re.search(r'<div>(.*?)</div>', s)
    print(res2.group(1)) # aaa

    # 匹配所有div内容(findall)
    res3 = re.findall(r'<div>(.*?)</div>', s)
    print(res3) # ['aaa', 'bbb']

9. 问题:统计字符串中每个字符的出现次数,要求按次数降序排列。

陷阱分析

新手容易遗漏 “空字符串”“大小写敏感”“特殊字符” 等边界情况,或排序逻辑错误。

正确解答

def count_char(s):
# 处理空字符串
if not s:
return {}
# 1. 统计字符次数(字典)
char_count = {}
for char in s:
char_count[char] = char_count.get(char, 0) + 1
# 2. 按次数降序、字符升序排序
sorted_items = sorted(char_count.items(), key=lambda x: (-x[1], x[0]))
# 3. 转换为字典(Python3.7+字典有序)
return dict(sorted_items)

# 测试
print(count_char("abracadabra")) # {'a': 5, 'b': 2, 'r': 2, 'c': 1, 'd': 1}
print(count_char("")) # {}

10. 问题:判断一个字符串是否是回文字符串(忽略大小写、空格、标点)。

陷阱分析

新手常只判断原字符串是否反转相等,忽略 “预处理(去空格 / 标点、转小写)” 的步骤。

正确解答

import re

def is_palindrome(s):
# 预处理:去除所有非字母数字字符,转小写
s_clean = re.sub(r'[^a-zA-Z0-9]', '', s).lower()
# 切片反转对比(最优方式)
return s_clean == s_clean[::-1]

# 测试
print(is_palindrome("A man, a plan, a canal: Panama")) # True
print(is_palindrome("race a car")) # False
print(is_palindrome("12321")) # True

11. 问题:去除字符串中所有空格(包括首尾、中间、制表符、换行符),有哪些方法?

正确解答

s = " hello \\t world \\n "

# 方法1:replace(仅去除普通空格)
res1 = s.replace(" ", "") # 'hello\\tworld\\n'

# 方法2:正则(去除所有空白字符:空格、制表符、换行符等,推荐)
import re
res2 = re.sub(r'\\s', '', s) # 'helloworld'

# 方法3:split + join(仅去除普通空格)
res3 = ''.join(s.split()) # 'helloworld'

print(res2) # 最终推荐:helloworld

12. 问题:字符串反转有哪些方法?哪个效率最高?

正确解答
  • 方法 1:切片[::-1](最优,底层 C 实现,效率最高)

    s = "abcdef"
    res = s[::-1] # 'fedcba'

  • 方法 2:reversed() + join(效率次之,适合迭代场景)

    res = ''.join(reversed(s)) # 'fedcba'

  • 方法 3:列表reverse() + join(效率最低,需转换类型)

    lst = list(s)
    lst.reverse()
    res = ''.join(lst) # 'fedcba'

  • 结论:优先用切片[::-1]反转字符串。

13. 问题:UnicodeDecodeError 是什么原因导致的?如何解决?

陷阱分析

新手常混淆 “Unicode” 和 “UTF-8”,或读取文件时不指定编码,导致解码失败。

正确解答
  • 核心概念:

    • Unicode:字符集(给每个字符分配唯一的数字编号,如'中'→U+4E2D);

    • UTF-8:编码规则(将 Unicode 编号转换为字节序列,如'中'→b'\\xe4\\xb8\\xad')。

  • 错误原因:解码时使用的编码格式,与字符串的实际编码格式不匹配(如用gbk解码 UTF-8 编码的字节)。

  • 常见场景 & 解决方案:

    # 场景1:读取文件时编码不匹配
    # 错误:open默认用系统编码(如Windows的gbk),读取UTF-8文件会报错
    # f = open("file.txt", "r") # UnicodeDecodeError
    # 正确:指定编码
    f = open("file.txt", "r", encoding="utf-8")

    # 场景2:字节解码时编码错误
    b = b'\\xe4\\xb8\\xad' # UTF-8编码的'中'
    # 错误:用gbk解码
    # s = b.decode("gbk") # UnicodeDecodeError
    # 正确:用UTF-8解码
    s = b.decode("utf-8") # '中'

    # 场景3:容错处理(无法确定编码时)
    s = b.decode("utf-8", errors="ignore") # 忽略错误字符
    # 或
    s = b.decode("utf-8", errors="replace") # 用�替换错误字符

14. 问题:encode() 和 decode() 的区别?为什么 Python3 中字符串默认是 Unicode?

正确解答
  • encode():字符串(Unicode)→ 字节串(bytes),如s.encode("utf-8"),用于 “存储 / 传输” 字符串;

  • decode():字节串(bytes)→ 字符串(Unicode),如b.decode("utf-8"),用于 “读取 / 解析” 字节数据;

  • Python3 设计逻辑:字符串(str)默认以 Unicode 存储,脱离具体编码,仅在 IO 操作(读写文件、网络传输)时才需要编码为 bytes,避免了 Python2 的编码混乱问题。

15. 问题:Python 的字符串驻留机制(String Interning)是什么?如何强制驻留?

正确解答
  • 核心原理:Python 通过intern_table哈希表缓存符合规则的小字符串,避免重复创建相同内容的字符串对象,节省内存。

  • 缓存规则(默认):

  • 字符串长度≤20;

  • 仅包含字母、数字、下划线(a-z, A-Z, 0-9, _);

  • 非动态生成(如str(123)、s1 + s2生成的字符串不缓存)。

  • 强制驻留:使用sys.intern()突破规则限制,强制将字符串加入驻留表:

    import sys

    s1 = "hello world!" # 含空格,不满足默认规则
    s2 = "hello world!"
    print(s1 is s2) # False

    # 强制驻留
    s1_intern = sys.intern(s1)
    s2_intern = sys.intern(s2)
    print(s1_intern is s2_intern) # True

赞(0)
未经允许不得转载:171主机测评 » Python3-数据类型-字符串(string)+常见面试题
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址