一、开篇:字符串——你最常打交道的类型
如果说整数是数字世界的基础,那字符串就是文字世界的基础。在编程中,你处理的绝大多数数据——用户名、邮件地址、网页内容、配置文件、JSON响应——本质上都是字符串。
Python的字符串处理能力在编程语言中是第一梯队的。它内置了非常丰富的字符串操作方法和格式化工具,让文本处理变得优雅而高效。
这篇文章,我们先从最基础的开始:字符串的创建方式。后面会有多篇文章分别深入讲解字符串的切片、常用方法、格式化等专题。
二、字符串创建的几种方式
2.1 单引号
# 最简单的字符串创建方式——用单引号 name = '小明' message = 'Hello, Python!' # 单引号字符串中如果想包含单引号,需要转义 text = 'I\'m a Python developer' print(text) # I'm a Python developer
2.2 双引号
# 和单引号等价,但各有各的方便之处 greeting = "你好,世界!" path = "D:\\Projects\\Python" # 双引号内可以直接使用单引号,不需要转义 text = "I'm a Python developer" # 简洁! print(text) # I'm a Python developer # 单引号内可以直接使用双引号 quote = '他说:"Python很有趣!"' print(quote) # 他说:"Python很有趣!"
选择单引号还是双引号?Python中两者完全等价。一般建议:
- 字符串中包含双引号时用单引号
- 字符串中包含单引号时用双引号
- 什么都没有时,选一种风格在整个项目中保持一致
2.3 三引号(多行字符串)
# 三个单引号 long_text = '''这是第一行 这是第二行 这是第三行''' print(long_text) # 三个双引号(效果相同) poem = """静夜思 床前明月光, 疑是地上霜。 举头望明月, 低头思故乡。""" print(poem) # 三引号的换行是保留的 multiline = """第一行 第二行 第三行""" print(repr(multiline)) # '第一行\n第二行\n第三行'
三引号的两个主要用途:
- 多行字符串:包含换行的长文本
- 文档字符串(docstring):函数、类、模块的文档说明
2.4 str() 构造函数
# 将其他类型转为字符串
print(str(42)) # '42'
print(str(3.14159)) # '3.14159'
print(str(True)) # 'True'
print(str(None)) # 'None'
print(str([1, 2, 3])) # '[1, 2, 3]'
print(str({'a': 1})) # "{'a': 1}"
# 自定义类的字符串表示
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
"""用户友好的字符串表示"""
return f'{self.name}({self.age}岁)'
def __repr__(self):
"""开发者友好的字符串表示"""
return f"Person(name='{self.name}', age={self.age})"
person = Person('小明', 25)
print(str(person)) # 小明(25岁)
print(repr(person)) # Person(name='小明', age=25)
2.5 原始字符串(r-string)
# 普通字符串中,反斜杠是转义字符
path1 = "C:\\Users\\小明\\Documents" # 需要双反斜杠
path2 = r"C:\Users\小明\Documents" # 原始字符串,一个反斜杠即可
print(path1) # C:\Users\小明\Documents
print(path2) # C:\Users\小明\Documents
# 正则表达式是原始字符串最主要的应用场景
import re
pattern = r'\d{3}-\d{4}-\d{4}' # 匹配电话号码
text = '我的电话是138-1234-5678'
match = re.search(pattern, text)
print(match.group() if match else '未匹配') # 138-1234-5678
原始字符串的注意事项:原始字符串中,反斜杠仍然不能出现在字符串末尾:
# r'hello\' # SyntaxError!即使原始字符串,末尾也不能是单个反斜杠 r'hello\\' # OK
2.6 f-string(格式化字符串)
# f-string是Python 3.6引入的,目前最推荐的格式化方式
name = '小明'
age = 25
score = 95.5
# 基本用法
print(f'我叫{name},今年{age}岁')
# 表达式求值
print(f'明年我就{age + 1}岁了')
print(f'我的分数是{score},等级是{"优秀" if score >= 90 else "良好"}')
# 格式化数字
pi = 3.141592653589793
print(f'π ≈ {pi:.2f}') # 保留2位小数
print(f'π ≈ {pi:.4f}') # 保留4位小数
# 调用方法
name = 'python'
print(f'{name.upper()}') # PYTHON
print(f'{name.capitalize()}') # Python
# 调试模式(Python 3.8+):{变量=}
print(f'{name=} {age=} {score=}')
# 输出:name='python' age=25 score=95.5
三、字符串的特性
3.1 不可变性
字符串是不可变对象——一旦创建,字符串的内容就不能被修改:
name = 'Pithon' # name[1] = 'y' # TypeError! 字符串不能直接修改 # ✅ 正确的"修改"方式——创建新字符串 name = name[:1] + 'y' + name[2:] print(name) # Python
每次对字符串的"修改"实际上都会创建一个新的字符串对象。这个特性确保了字符串在多处引用时的安全性。
3.2 有序的字符序列
字符串是一个有序的字符序列,这意味着:
- 每个字符都有固定的位置(索引从0开始)
- 可以通过索引访问单个字符
- 可以切片获取子串
- 可以遍历每个字符
text = 'Hello'
# 索引访问
print(text[0]) # H(第一个字符)
print(text[-1]) # o(最后一个字符)
# 遍历每个字符
for char in text:
print(char, end=' ') # H e l l o
print()
# 长度
print(len(text)) # 5
3.3 Unicode支持
Python 3的字符串以Unicode存储,天然支持各种语言和符号:
# 各种语言的文字
chinese = '你好,世界!'
japanese = 'こんにちは、世界!'
arabic = 'مرحبا بالعالم'
emoji = '🐍 Python 🚀'
print(chinese) # 你好,世界!
print(emoji) # 🐍 Python 🚀
# 获取字符的Unicode码点
print(ord('中')) # 20013
print(ord('🐍')) # 128013
# 从码点获取字符
print(chr(20013)) # 中
print(chr(128013)) # 🐍
# 完整的Unicode范围
for code in range(128013, 128018):
print(f'U+{code:05X}: {chr(code)}')
四、转义字符
4.1 常用转义字符
print('Hello\nWorld') # \n:换行
print('Hello\tWorld') # \t:制表符(Tab)
print('Hello\\World') # \\:反斜杠本身
print('I\'m learning') # \':单引号
print("He said:\"Hi!\"") # \":双引号
print('Hello\rWorld') # \r:回车(很少用)
print('Hello\bWorld') # \b:退格(很少用)
4.2 Unicode转义
# 16位Unicode转义(\u + 4位十六进制)
print('你好') # 你好
# 32位Unicode转义(\U + 8位十六进制)
print('\U0001f40d') # 🐍(蛇的emoji)
# 八进制转义(\ + 3位八进制数)
print('\101') # A(八进制101 = 十进制65 = ASCII 'A')
# 十六进制转义(\x + 2位十六进制)
print('\x41') # A
五、字符串拼接与重复
5.1 用 + 拼接
first_name = '张' last_name = '三' full_name = last_name + first_name print(full_name) # 张三 # 字符串 + 其他类型会报错 age = 25 # message = '我今年' + age + '岁' # TypeError! # 需要先转成字符串 message = '我今年' + str(age) + '岁' print(message) # 我今年25岁
5.2 用 * 重复
print('-' * 40) # 40个减号组成的分隔线
print('你好' * 3) # 你好你好你好
print('=' * 5 + '标题' + '=' * 5) # =====标题=====
5.3 隐式拼接
这个特性很多人不知道——相邻的字符串字面量会自动拼接:
# Python会自动拼接相邻的字符串字面量
message = 'Hello, ' 'World!' ' 你好!'
print(message) # Hello, World! 你好!
# 这在写长字符串时很有用
long_text = (
'这是一个很长的字符串,'
'它被分成了多行来写,'
'但Python会自动把它们拼起来。'
)
print(long_text) # 一行显示
# 注意:只对字符串字面量有效,对变量无效
a = 'Hello'
b = 'World'
# result = a b # SyntaxError!
result = a + b # OK
5.4 join() 方法
对于拼接多个字符串(特别是拼接列表中的字符串),join() 比 + 更高效:
words = ['Python', 'Java', 'Go', 'Rust']
# 用join拼接——高效
result = ', '.join(words)
print(result) # Python, Java, Go, Rust
# 不用join拼接——低效(每次+都创建新字符串)
result = ''
for word in words:
result += word + ', '
result = result[:-2] # 去掉最后的逗号和空格
print(result) # Python, Java, Go, Rust
# URL路径拼接
path_parts = ['api', 'v1', 'users', '123']
url = '/'.join(path_parts)
print(url) # api/v1/users/123
# HTML标签拼接
items = ['苹果', '香蕉', '橘子']
html = '<ul>\n' + '\n'.join(f' <li>{item}</li>' for item in items) + '\n</ul>'
print(html)
六、字符串的编码与解码
6.1 字符串(str) vs 字节串(bytes)
这是很多初学者困惑的地方。关键区别:
str(字符串):人类可读的Unicode文本bytes(字节串):机器存储/传输的原始字节
# str → bytes:编码(encode)
text = '你好,Python!'
encoded = text.encode('utf-8')
print(encoded) # b'\xe4\xbd\xa0\xe5\xa5\xbd\xef\xbc\x8cPython\xef\xbc\x81'
print(type(encoded)) # <class 'bytes'>
# bytes → str:解码(decode)
decoded = encoded.decode('utf-8')
print(decoded) # 你好,Python!
print(type(decoded)) # <class 'str'>
6.2 常见编码格式
text = '你好,世界!'
# UTF-8(最通用的编码,建议默认使用)
utf8_bytes = text.encode('utf-8')
print(f'UTF-8: {utf8_bytes}({len(utf8_bytes)}字节)')
# GBK/GB2312(中文Windows系统常用)
gbk_bytes = text.encode('gbk')
print(f'GBK: {gbk_bytes}({len(gbk_bytes)}字节)')
# UTF-16
utf16_bytes = text.encode('utf-16')
print(f'UTF-16: {utf16_bytes}({len(utf16_bytes)}字节)')
# ASCII(只支持英文字符,中文会报错)
# ascii_bytes = text.encode('ascii') # UnicodeEncodeError!
ascii_text = 'Hello World'
ascii_bytes = ascii_text.encode('ascii')
print(f'ASCII: {ascii_bytes}({len(ascii_bytes)}字节)')
文件操作中最常见的错误之一就是编码问题:
# 写文件时指定UTF-8编码
with open('output.txt', 'w', encoding='utf-8') as f:
f.write('中文内容')
# 读文件时用相同的编码
with open('output.txt', 'r', encoding='utf-8') as f:
content = f.read()
七、字符串创建的性能考量
7.1 + 拼接 vs join
import time
# 用+拼接大量字符串(慢)
words = ['hello'] * 10000
start = time.perf_counter()
result = ''
for word in words:
result += word
elapsed_plus = time.perf_counter() - start
print(f'+ 拼接耗时:{elapsed_plus:.4f}秒')
# 用join(快得多)
start = time.perf_counter()
result = ''.join(words)
elapsed_join = time.perf_counter() - start
print(f'join 耗时:{elapsed_join:.4f}秒')
# 速度差异可能达到上百倍!
原因:由于字符串不可变,每次 += 都会创建新的字符串对象并拷贝旧内容。join() 只需要分配一次内存。
7.2 用列表收集,然后join
# ❌ 频繁的字符串拼接
result = ''
for i in range(1000):
result += f'第{i}行\n'
# ✅ 用列表收集,最后一次性join
lines = []
for i in range(1000):
lines.append(f'第{i}行')
result = '\n'.join(lines)
八、判断字符串类型的方法
# 判断字符串的内容类型
print('abc'.isalpha()) # True — 全是字母
print('123'.isdigit()) # True — 全是数字
print('abc123'.isalnum()) # True — 字母或数字
print(' '.isspace()) # True — 全是空白字符
print('abc'.islower()) # True — 全是小写
print('ABC'.isupper()) # True — 全是大写
print('Abc'.istitle()) # True — 标题格式(每个单词首字母大写)
print('123'.isdecimal()) # True — 十进制数字
print('123'.isnumeric()) # True — 数字字符
print('abc'.isascii()) # True — 全是ASCII字符
# 这些判断方法在实际中非常有用
def validate_password(password):
"""验证密码复杂度"""
if len(password) < 8:
return False, '密码至少8位'
if not any(c.isupper() for c in password):
return False, '密码必须包含大写字母'
if not any(c.islower() for c in password):
return False, '密码必须包含小写字母'
if not any(c.isdigit() for c in password):
return False, '密码必须包含数字'
return True, '密码符合要求'
print(validate_password('abc')) # (False, '密码至少8位')
print(validate_password('abcdefgh')) # (False, '密码必须包含大写字母')
print(validate_password('Abcdefgh1')) # (True, '密码符合要求')
九、本篇小结
字符串是Python中最常用的类型之一。本篇的核心收获:
- 六种创建方式:单引号、双引号、三引号、str()、r-string、f-string
- 不可变性:字符串创建后不可修改,所有"修改"都创建新字符串
- Unicode原生支持:中文、日文、emoji都原生支持
- 转义字符:
\n换行、\t制表、\\反斜杠、Unicode转义\u和\U - 拼接方式:
+少量拼接、join()大量拼接(性能差距巨大) - 编码与解码:
encode()和decode(),UTF-8最通用
字符串的创建是最基础的操作,但很多开发者在性能问题上踩坑(比如在循环中用+拼接大量字符串)。













