字符串格式化
%格式化
基本语法:模板字符串 % (值1, 值2,...)
模板字符串里的占位符以%+类型说明符表示,具体占位符如下
| 占位符 | 含义 |
|---|---|
| %s | 字符串 |
| %r | 原始调用(repr()) |
| %d | 十进制整数 |
| %f | 浮点数 |
%占位符还支持通过格式说明符控制输出格式,语法如下:
%[宽度][.精度][类型]
- 宽度指定输出字符串的最小长度,不足用空格填充,默认右对齐
- 精度:对浮点数表示小数位数,对字符串表示最大长度
- 左对齐:宽度前加
-实现左对齐
不好用
str.format()
基本语法与参数
基本语法:模板字符串.format(参数1, 参数2, ..., 关键字参数1=值1, ...)
模板字符串中使用{}占位符在模板中预留位置
位置参数:占位符中可以用数字指定参数索引,或省略按顺序匹配
1
2
3
4
5
# 按顺序匹配
print("Name: {}, Age: {}".format("lihua", 18))
# 按索引指定(可重复/乱序)
print("Second: {1}, First: {0}, First again: {0}".format("A", "B"))关键字参数:占位符中也可以直接用关键字参数名
1
print("User: {name}, ID: {id}".format(name="lihua", id=114514))混合使用:可以混合使用,但是位置参数必须在关键字参数前
1
print("{}: {name} ({age})".format("person", name="lihua", age=18))格式规范和类型转换
format方法支持通过:在占位符中添加格式规范,语法形如{[名称/索引]:[格式规范]}
对齐和宽度:
<是左对齐>是右对齐^是居中对齐- 符号后可以添加数字指定对齐后的字符串长度
浮点数精度:
{:.位数f}千位分隔符:
{:,},只适用于数字类型转换符:
{!类型},指定类型转换,类似于%s或%r,对应字母一样特定类型格式化:
- 日期:
{:%Y-%m-%d %H:%M} - 百分比:
{:.位数%}
- 日期:
嵌套与自定义
- 可以嵌套列表和字典:
1
2
3
4
stu = {"name": "lihua", "age": 18}
classes = ["class1", "class2"]
print("Name: {s['name']}, Hobby: {c[0]}".format(s=stu, c=classes))- 可以调用对象方法:可以调用无参数的对象方法
1
2
text = "hello world"
print("{.upper()}".format((text)))- 自定义格式化逻辑:在类中实现
__format__方法,可以自定义对象的格式化行为
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Temperature:
def __init__(self, celsius):
self.celsius = celsius
# 自定义格式化:支持 'c'(摄氏度)和 'f'(华氏度)
def __format__(self, format_spec):
if format_spec == 'f':
fahrenheit = self.celsius * 9/5 + 32
return f"{fahrenheit:.1f}°F"
else: # 默认摄氏度
return f"{self.celsius:.1f}°C"
temp = Temperature(25)
print("Temp: {t:c}".format(t=temp)) # 输出:Temp: 25.0°C
print("Temp: {t:f}".format(t=temp)) # 输出:Temp: 77.0°Ff-string
格式化字符字面值,在字符串前加f或F前缀,在字符串中用{}嵌入变量或表达式
它的性能更快,安全性更高
基本语法与格式控制
f"模板字符串, {变量/表达式}会被替换为值"
1
2
3
name = 'lihua'
age = 18
print(f"name: {name}, age: {age}")格式控制语法:{表达式:格式规范}
与format相同的格式规范
高级特性
f-string
支持在{}中嵌入任意合法的Python表达式,包括函数调用、三目运算等
- 函数调用:直接在占位符中调用函数
1
2
3
4
def hello(name):
return f"hello {name}"
name = 'lihua'
print(f"{hello(name)}")- 三目运算:
1
2
score = 80
print(f{'pass' if score>=60 else 'failed'})- 嵌套结构访问:
1
2
3
stu = {"name": "lihua", "age": 18}
classes = ["class1", "class2"]
print(f"student: {stu['name']}, class: {class[0]}")- 多行f-string:使用三引号支持多行模板
1
2
3
4
5
6
7
8
9
name = 'lihua'
age = 18
score = 80
bio = f"""
student info:
- name: {name}
- age: {age}
- score: {'pass' if score>=60 else 'failed'}
"""异常处理
Python对异常的表现
当发生异常时,python会生成一个异常对象,包含错误类型、描述和发生位置
若未捕获,程序终止并打印回溯信息(Traceback),显示异常类型和发生位置
常见异常
常见异常类型如下:
| 异常类型 | 说明 |
|---|---|
SyntaxError |
语法错误 |
NameError |
使用了未定义的变量 |
TypeError |
操作或函数应用于不适当类型 |
ValueError |
参数类型正确,但值不合法 |
IndexError |
索引超出序列范围 |
KeyError |
字典中不存在指定键 |
AttributeError |
对象没有指定属性或方法 |
ZeroDivisionError |
除数为 0 |
FileNotFoundError |
文件不存在 |
IOError / OSError |
输入输出操作错误 |
ImportError |
导入模块失败 |
ModuleNotFoundError |
找不到指定模块 |
EOFError |
输入到达文件末尾 |
RuntimeError |
一般运行时错误 |
MemoryError |
内存不足 |
OverflowError |
数值运算结果超出范围 |
RecursionError |
递归层数超过限制 |
AssertionError |
assert 断言失败 |
StopIteration |
迭代器没有更多元素 |
KeyboardInterrupt |
用户按下 Ctrl+C 中断程序 |
处理异常的语法
Python使用try-catch语句块捕获并处理异常,结构如下:
1
2
3
4
5
6
7
8
9
10
try:
risk_operation()
except 异常类型1:
# 处理异常1的代码
except 异常类型2 as e:
# 处理异常2的代码,e是异常对象
else:
# 无异常时执行的代码(可选)
finally:
# 无论是否有异常都执行的代码(可选)获取异常信息
使用as e捕获异常对象,进而提取异常详情
1
2
3
4
5
6
try:
int('a')
except ValueError as e:
print("异常类型: ", type(e))
print("异常描述: ", e)
print("异常参数: ", e.args)捕获多个异常
- 一个try后可以接多个catch,处理不同异常
- 使用
except Exception捕获所有非系统退出类异常
异常的主动触发与传递
主动触发异常
使用raise可以手动触发异常,用于验证合法性或标记未实现的功能
1
2
3
4
5
6
7
8
9
def register(username):
if len(username) < 3:
# 主动触发 ValueError
raise ValueError(f"用户名 '{username}' 太短,至少需要3个字符")
print(f"'{username}' 注册成功")
try:
register("ab") # 长度不足,触发异常
except ValueError as e:重新抛出异常
捕获异常后,若无法处理,可以用raise重新抛出,让上层代码处理,此时raise不带参数
1
2
3
4
5
6
7
8
9
10
11
12
ef read_data(filename):
try:
with open(filename, "r") as f:
return f.read()
except FileNotFoundError as e:
print(f"read_data 函数捕获到错误:{e}")
raise # 重新抛出异常,让调用者处理
try:
data = read_data("nonexist.txt")
except FileNotFoundError as e:
print(f"主程序处理错误:{e}")异常的传递
若函数内发生异常且未捕获,异常会向上传递至调用者,直至被捕获或程序崩溃
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def f3():
print("进入f3")
x = int("abc") # ValueError
print("离开f3")
def f2():
print("进入f2")
f3()
print("离开f2")
def f1():
print("进入f1")
f2()
print("离开f1")
try:
f1()
except ValueError as e:
print("捕获异常:", e)结果为:
1
2
3
4
进入f1
进入f2
进入f3
捕获异常: invalid literal for int() with base 10: 'abc'如果想要在中间层处理后继续传递,使用raise
自定义异常
定义
当内置异常无法满足要求时,可以继承Exception类自定义异常
1
2
3
4
5
6
7
8
9
10
11
# 定义自定义异常(继承 Exception)
class InvalidAgeError(Exception):
"""当年龄不合法时触发的异常"""
def __init__(self, age, message="年龄必须在 0-120 之间"):
self.age = age
self.message = message
super().__init__(self.message) # 调用父类构造函数
# 可选:自定义异常描述
def __str__(self):
return f"{self.message},实际输入:{self.age}"触发
1
2
3
4
5
6
7
8
9
10
def check_age(age):
if not (0 <= age <= 120):
# 触发自定义异常
raise InvalidAgeError(age)
print(f"年龄 {age} 合法")
try:
check_age(150) # 超出范围,触发异常
except InvalidAgeError as e:
print("检查失败:", e) # 输出:检查失败:年龄必须在 0-120 之间,实际输入:150继承
自定义异常可以继承,用于创建更细分的异常类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 基础异常
class ValidationError(Exception):
pass
# 细分异常(继承自 ValidationError)
class TooSmallError(ValidationError):
pass
class TooLargeError(ValidationError):
pass
def validate_number(n):
if n < 0:
raise TooSmallError(f"{n} 不能小于 0")
if n > 100:
raise TooLargeError(f"{n} 不能大于 100")
print(f"{n} 验证通过")
try:
validate_number(-5)
except TooSmallError as e:
print("处理过小错误:", e)
except TooLargeError as e:
print("处理过大错误:", e)
except ValidationError as e: # 捕获所有子类异常
print("处理其他验证错误:", e)文件操作
基础文件操作
open()函数是文件操作的入口,用于打开文件返回文件对象,语法如下:
1
open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)file是文件路径,传入字符串或Path对象mode是打开模式,决定读写权限和文件类型encoding是文本模式下的编码格式
文件打开模式
- 只读:r
- 只写:w
- 追加:a
- 读写(保留原内容):r+
- 读写(清空原内容):w+
- 追加+读:a+
基础操作
需要遵循“打开->操作->关闭”的流程,防止资源泄露
一般使用with语句(即上下文管理器)自动关闭文件,无需手动关闭
1
2
3
4
5
6
7
# 写入
with open('test.txt', 'w', encoding='utf-8') as f:
f.write('hello world!\n')
# 读取
with open('test.txt', 'r', encoding='utf-8') as f:
content = f.read()
print(content)二进制文件
二进制文件需要在操作后加一个b(有加号的在加号前)
1
2
3
with open('test.jpg', 'rb') as src, open('copy.jpg', 'wb') as dst:
data = src.read()
dst.write(data)文件读取方式
| 方法 | 功能说明 | 适用场景 |
|---|---|---|
read() |
无参:读取全部内容; 有参 n:读取至多
n 字节(二进制模式常用) |
小文件(一次性处理全文); 二进制大文件分块处理(如网络传输、分片复制) |
readline() |
读取一行内容(含换行符 \n),支持指定字节限制
limit |
按行处理文件(如日志解析、CSV逐行读取);超大行文件(指定
limit 控制内存) |
readlines() |
读取所有行,返回列表(每行作为元素,含换行符 \n) |
中等大小文件,需按行索引访问(如按行号提取内容)或批量处理所有行 |
迭代文件对象 (for line in f) |
自动逐行读取,循环内每次返回一行(含换行符
\n),底层调用 readline() |
大/超大文件(内存友好,无需一次性加载全部内容),仅需顺序处理行数 |
文件写入方式
| 方法 | 功能说明 | 适用场景 |
|---|---|---|
write(str) |
写入字符串,返回写入的字符数 | 单次写入一个完整的字符串;需要精确控制写入内容(如构建自定义格式) |
writelines(seq) |
写入字符串序列(列表/元组等),不自动添加换行符 | 批量写入多个字符串;当序列中的每个元素己包含换行符,或不需要分隔符时 |
print(..., file=f) |
用 print 函数写入,可自动添加换行符(end
参数控制) |
快速写入并自动换行;需要灵活控制输出格式(如自定义分隔符、末尾字符) |
文件指针
| 方法/属性 | 功能说明 |
|---|---|
tell() |
返回当前指针位置(字节数) |
seek(offset, whence) |
移动指针到指定位置:offset
偏移量(正数向后,负数向前);whence
基准位置(0:文件开头,1:当前位置,2:文件末尾) |
e.g.
1
2
3
4
5
6
7
8
9
10
11
12
with open('test.txt', 'w+', encoding='utf-8') as f:
f.write('0123456789')
print('写后指针位置:', f.tell()) # 指针此时在文件末尾,输出10
# 从当前位置移动
f.seek(2,1) # 从当前位置开始计算,移动两位
print(f.read())
print(f.tell())
# 从文件末尾移动
f.seek(-3,2) # 从末尾开始计算,移动三位
print(f.read())文件属性和元数据
通过os或pathlib库可以获取文件的元数据
- Path():路径类,可以便捷创建路径
- .exists():判断文件是否存在
- .is_file():判断是否为文件
- .is_dir():判断是否为目录
- Path().mkdir(parents=True, exist_ok=True):创建目录,parents为自动创建上层目录,exist——ok为存在时不报错
- .stat().st_size:文件大小(字节)
- .stat().st_ctime:创建时间
- .stat().st_mtime:最后修改时间