YAML 从入门到应用:轻松搞定配置管理与数据序列化咱们先唠唠,你是不是也遇到过这种情况:写 Python 项目时,想把配置(比如数据库地址、参数设置)和代码分开,不然每次改配置都要动代码,太麻烦了;或者想存点结构化数据,JSON 虽然能用,但写注释、处理多行文本总觉得别扭?
这时候 YAML 就派上用场了!今天咱们就从 0 开始,把 Python 里用 YAML 的那点事儿讲透,不管你是新手还是有点经验,看完都能直接上手用。
1. 认识 YAML:到底是个啥?有啥优点?先搞明白基础:YAML 不是什么高深技术,就是一种 “人类能轻松看懂” 的数据序列化语言。简单说,就是把 Python 里的字典、列表这些数据,写成文件存起来;反过来,也能把文件里的内容读成 Python 能直接用的数据。
它的优点特别实在,咱们列个表更清楚:
优点
大白话解释
简单易读
不用写大括号、中括号(比如 JSON 的 {}[]),靠缩进就能表示结构,像写笔记一样
语言支持广
不光 Python 能用,Java、Go、JavaScript 都支持,跨语言传数据方便
支持多种数据类型
字符串、数字、布尔值这些基础类型,还有字典、列表,甚至嵌套结构都能搞定
能加注释
用 #开头写注释,以后看自己写的配置,一眼就知道啥意思
适合配置和数据存储
存配置文件(比如项目参数)、数据文件(比如翻译内容)都特别合适
2. 准备工作:先装个 pyyaml 库Python 本身不自带处理 YAML 的工具,得装个第三方库叫pyyaml,跟装其他库一样简单,就用pip命令。
安装步骤(保证能运行):打开你的命令行(Windows 是 CMD 或 PowerShell,Mac/Linux 是终端)直接输下面这个命令,按回车:代码语言:python复制pip install pyyaml等个几秒钟,看到 “Successfully installed pyyaml-x.x.x”(x.x.x 是版本号)就装好了。常见安装问题:要是提示 “权限不够”(比如 Linux/Mac),就在命令前面加sudo:代码语言:python复制sudo pip install pyyaml要是提示 “pip 不是内部命令”,那是你 Python 没配置环境变量,或者直接用python -m pip代替pip:代码语言:python复制python -m pip install pyyaml3. 第一步:创建你的第一个 YAML 文件YAML 文件的后缀一般是.yaml(推荐)或者.yml,随便用个文本编辑器就能写(比如记事本、VSCode、Sublime)。
咱们先写个最简单的,叫first.yaml,内容如下:
代码语言:python复制# 这是注释,#后面的内容不会被解析
name: 张三 # 键是name,值是字符串“张三”
age: 25 # 键是age,值是整数25
is_student: false # 键是is_student,值是布尔值false
score: 98.5 # 键是score,值是浮点数98.5
hobbies: # 键是hobbies,值是列表(用-开头表示列表元素)
- 篮球
- 编程
- 看电影
address: # 键是address,值是字典(嵌套结构,靠缩进)
province: 广东
city: 深圳
detail: 某小区12栋注意两个关键规则(刚开始容易错):
缩进敏感:YAML 靠缩进来表示层级,必须用空格,不能用 Tab!一般缩进 2 个或 4 个空格(推荐 2 个,统一就行)。键值对格式:键和值之间必须用冒号 + 空格(比如name: 张三,冒号后没空格会报错)。4. 第二步:把 YAML 文件加载到 Python 里写好 YAML 文件后,怎么在 Python 里读它呢?就用刚装的pyyaml库,步骤特别固定,咱们写个 Python 脚本load_yaml.py:
代码示例(保证能运行):代码语言:python复制import yaml # 导入pyyaml库
# 方法1:读取外部YAML文件(最常用)
def load_yaml_file(file_path):
try:
# 用open打开文件,指定编码utf-8(避免中文乱码)
with open(file_path, 'r', encoding='utf-8') as f:
# 用safe_load加载,重点:别用load!后面讲为什么
data = yaml.safe_load(f)
return data
except FileNotFoundError:
print(f"报错:没找到{file_path}这个文件!")
return None
except Exception as e:
print(f"加载YAML出错了:{e}")
return None
# 调用函数,加载咱们刚才写的first.yaml
yaml_data = load_yaml_file("first.yaml")
# 打印结果,看看是不是Python能直接用的数据
if yaml_data:
print("加载出来的Python数据:")
print(yaml_data)
print("n单独取某个值:")
print(f"名字:{yaml_data['name']}")
print(f"年龄:{yaml_data['age']}")
print(f"第一个爱好:{yaml_data['hobbies'][0]}")
print(f"城市:{yaml_data['address']['city']}")怎么运行:把first.yaml和load_yaml.py放在同一个文件夹里命令行进入这个文件夹,输:代码语言:python复制python load_yaml.py运行结果:你会看到这样的输出,说明 YAML 里的数据已经变成 Python 的字典、列表了:
代码语言:python复制加载出来的Python数据:
{'name': '张三', 'age': 25, 'is_student': False, 'score': 98.5, 'hobbies': ['篮球', '编程', '看电影'], 'address': {'province': '广东', 'city': '深圳', 'detail': '某小区12栋'}}
单独取某个值:
名字:张三
年龄:25
第一个爱好:篮球
城市:深圳5. YAML 数据类型:从简单到复杂(必掌握)YAML 支持的所有数据类型,咱们用表格整理清楚,每个类型都给 YAML 写法和对应的 Python 结果,看完直接能套用。
5.1 基础数据类型数据类型
YAML 写法示例
Python 对应类型
Python 加载结果
注意事项
字符串
name: 张三name: "张三"name: ' 张三'
str
' 张三'
一般不用引号,有特殊字符(比如空格、#)才用
整数
age: 25count: -10
int
25-10
直接写数字,不用加引号
浮点数
score: 98.5pi: 3.14e2
float
98.5314.0
支持科学计数法(3.14e2=314)
布尔值
is_ok: trueis_no: false
bool
TrueFalse
不区分大小写(TRUE 也可以),但推荐小写
Null 值
empty: ~none_val: null
None
None
~ 或 null 都表示空,Python 里是 None
5.2 复杂数据类型(字典、列表)这俩是最常用的,尤其是嵌套结构,咱们分开讲:
(1)字典(键值对集合)YAML 里的字典就是 “键:值” 的组合,嵌套字典靠缩进:
代码语言:python复制# YAML写法
person:
name: 李四
age: 30
contact: # 嵌套字典
phone: 13800138000
email: lisi@example.com加载到 Python 后是这样的字典:
代码语言:python复制{'person': {'name': '李四', 'age': 30, 'contact': {'phone': '13800138000', 'email': 'lisi@example.com'}}}(2)列表(有序元素集合)YAML 里的列表用-开头,每个-代表一个元素,嵌套列表也靠缩进:
代码语言:python复制# YAML写法
fruits: # 普通列表
- 苹果
- 香蕉
- 橙子
students: # 嵌套列表(列表里套字典)
- name: 王五
age: 22
- name: 赵六
age: 23加载到 Python 后是这样的:
代码语言:python复制{'fruits': ['苹果', '香蕉', '橙子'], 'students': [{'name': '王五', 'age': 22}, {'name': '赵六', 'age': 23}]}5.3 测试代码(验证数据类型)写个test_types.yaml,把上面的类型都放进去:
代码语言:python复制# 基础类型
string_val: 测试字符串
int_val: 100
float_val: 3.14
bool_val: true
null_val: ~
# 复杂类型
dict_val:
key1: val1
key2: val2
list_val:
- 元素1
- 元素2
nested_val: # 嵌套(字典里套列表)
class: 三年级1班
students:
- 小明
- 小红再写个test_types.py加载它:
代码语言:python复制import yaml
with open("test_types.yaml", 'r', encoding='utf-8') as f:
data = yaml.safe_load(f)
# 打印每个值的类型和内容
print("string_val:", type(data['string_val']), "->", data['string_val'])
print("int_val:", type(data['int_val']), "->", data['int_val'])
print("float_val:", type(data['float_val']), "->", data['float_val'])
print("bool_val:", type(data['bool_val']), "->", data['bool_val'])
print("null_val:", type(data['null_val']), "->", data['null_val'])
print("dict_val:", type(data['dict_val']), "->", data['dict_val'])
print("list_val:", type(data['list_val']), "->", data['list_val'])
print("nested_val:", type(data['nested_val']), "->", data['nested_val'])运行后会看到每个值的类型,确认 YAML 和 Python 类型是对应的,比如bool_val是bool类型,dict_val是dict类型。
6. YAML 特色功能:这些技巧超实用!YAML 有几个特别好用的功能,学会了能省很多事,咱们一个个讲,每个都给示例。
6.1 加注释:让别人看懂你的配置这个简单,用#开头,后面的内容都是注释,YAML 解析的时候会忽略:
代码语言:python复制# 这是一个项目配置文件
# 作者:XXX
# 日期:不用写死,运行时Python能获取
# 数据库配置
db:
host: localhost # 数据库地址,本地测试用localhost
port: 3306 # MySQL默认端口
user: root # 用户名,生产环境别用root!
password: 123456 # 密码,实际项目要存在环境变量里,别写这注意:注释只能写在单独一行,或者键值对的后面,不能插在键和值中间(比如name: # 注释 张三这样会报错)。
6.2 多行字符串:处理大段文本(管道符 | vs 折叠符 >)有时候要存大段文本(比如日志模板、邮件内容),用字符串写一行太别扭,YAML 的多行字符串就很方便,关键是区分|(管道符)和>(折叠符)。
两者的区别(重点):|:保留所有换行和空格,写的时候是什么样,加载后就是什么样。>:把换行变成空格(除了最后一个换行),适合存一段连续的文本,不想换行。示例:写个multi_line.yaml代码语言:python复制# 管道符|:保留换行
text_with_newline: |
第一行文本
第二行文本(前面有2个空格)
第三行文本(前面有4个空格)
第四行文本
# 折叠符>:换行变空格
text_no_newline: >
这是一段很长的文本,
本来写了换行,
但用了折叠符后,
会变成一整行(最后会留一个换行)加载代码load_multi_line.py:代码语言:python复制import yaml
with open("multi_line.yaml", 'r', encoding='utf-8') as f:
data = yaml.safe_load(f)
print("=== 管道符|的结果(保留换行)===")
print(data['text_with_newline'])
print("=== 折叠符>的结果(换行变空格)===")
print(data['text_no_newline'])运行结果:你会明显看到区别:
代码语言:python复制=== 管道符|的结果(保留换行)===
第一行文本
第二行文本(前面有2个空格)
第三行文本(前面有4个空格)
第四行文本
=== 折叠符>的结果(换行变空格)===
这是一段很长的文本, 本来写了换行, 但用了折叠符后, 会变成一整行(最后会留一个换行)注意:多行字符串的缩进要跟父元素的缩进一致(比如上面text_with_newline下面的文本,缩进都是 2 个空格,跟text_with_newline的键对齐),不然会解析错。
6.3 锚点和别名:实现数据重用(避免重复写)如果 YAML 里有重复的数据(比如多个用户用同一个地址),不用每次都写一遍,用锚点(&) 标记重复数据,再用别名(*) 引用,省事又不容易错。
示例 1:简单引用(anchor1.yaml)代码语言:python复制# 用&定义锚点(anchor_address是锚点名字,随便起)
address: &anchor_address
province: 浙江
city: 杭州
street: 西湖路
# 用*引用锚点
user1:
name: 小明
addr: *anchor_address # 引用上面的address
user2:
name: 小红
addr: *anchor_address # 再引用一次加载结果:代码语言:python复制{'address': {'province': '浙江', 'city': '杭州', 'street': '西湖路'},
'user1': {'name': '小明', 'addr': {'province': '浙江', 'city': '杭州', 'street': '西湖路'}},
'user2': {'name': '小红', 'addr': {'province': '浙江', 'city': '杭州', 'street': '西湖路'}}}示例 2:合并字典(用 <<)如果想在引用的基础上改点数据(比如 user2 的街道不一样),可以用<<把锚点的字典合并进来,再覆盖需要改的键:
代码语言:python复制address: &anchor_address
province: 浙江
city: 杭州
street: 西湖路
user1:
name: 小明
<<: *anchor_address # 合并锚点的字典,相当于把province、city、street直接写在这里
user2:
name: 小红
<<: *anchor_address # 先合并
street: 钱江路 # 覆盖street的值加载结果:代码语言:python复制{'address': {'province': '浙江', 'city': '杭州', 'street': '西湖路'},
'user1': {'name': '小明', 'province': '浙江', 'city': '杭州', 'street': '西湖路'},
'user2': {'name': '小红', 'province': '浙江', 'city': '杭州', 'street': '钱江路'}}看,user2 的 street 变成了 “钱江路”,其他字段还是锚点里的,特别方便!
6.4 紧凑写法:字典用 {},列表用 [](简单数据专用)如果数据很简单,不想用缩进占太多行,可以用紧凑写法:字典用{}包起来,列表用[]包起来,跟 JSON 有点像,但 YAML 支持的更多。
示例(compact.yaml):代码语言:python复制# 紧凑字典:用{}
person: {name: 小李, age: 28, is_student: false}
# 紧凑列表:用[]
fruits: [苹果, 香蕉, 橙子]
# 混合紧凑:列表里套字典
students: [{name: 小王, age: 20}, {name: 小刘, age: 21}]加载结果:跟普通写法完全一样,就是写起来更短:
代码语言:python复制{'person': {'name': '小李', 'age': 28, 'is_student': False},
'fruits': ['苹果', '香蕉', '橙子'],
'students': [{'name': '小王', 'age': 20}, {'name': '小刘', 'age': 21}]}注意:紧凑写法只适合简单数据,复杂的嵌套还是用缩进写法,不然会乱。
7. 安全第一:别用 yaml.load ()!前面加载 YAML 的时候,我一直用yaml.safe_load(),没提yaml.load(),不是忘了,是yaml.load()有大风险!
为什么危险?yaml.load()默认会用一个叫Loader的解析器,这个解析器能执行 Python 代码!如果加载的 YAML 文件里有恶意代码(比如删除你电脑文件的命令),就会直接运行,后果很严重。
举个恐怖的例子:如果有人给你一个malicious.yaml,内容是这样的:
代码语言:python复制!!python/object/apply:os.system ["rm -rf /"]你要是用yaml.load()加载:
代码语言:python复制import yaml
with open("malicious.yaml", 'r') as f:
yaml.load(f) # 危险!会执行rm -rf /(删除Linux所有文件)在 Linux/Mac 上运行,你的文件就全没了!Windows 上虽然命令不生效,但也可能执行其他恶意操作。
正确的做法(必记):不管什么时候,都用yaml.safe_load(),或者指定yaml.SafeLoader:
代码语言:python复制# 方法1:推荐,简单
data = yaml.safe_load(f)
# 方法2:等价于safe_load,更明确
data = yaml.load(f, Loader=yaml.SafeLoader)safe_load()只会解析 YAML 的基础数据类型(字典、列表、字符串等),不会执行任何 Python 代码,绝对安全。
8. 实际应用:加载多语言翻译文件讲了这么多理论,咱们来个实际项目里常用的场景:多语言支持。比如你的程序要支持中文和英文,把翻译内容存在 YAML 里,用户选哪个语言就加载哪个文件,不用改代码。
步骤 1:创建翻译文件先建两个 YAML 文件,分别存中文和英文的翻译:
zh.yaml(中文翻译):代码语言:python复制welcome: "欢迎使用本程序!"
select_lang: "请选择语言(zh=中文,en=英文):"
input_error: "输入错误,请重新输入!"
exit_msg: "程序已退出,再见!"en.yaml(英文翻译):代码语言:python复制welcome: "Welcome to the program!"
select_lang: "Please select language (zh=Chinese, en=English): "
input_error: "Input error, please try again!"
exit_msg: "Program exited, goodbye!"步骤 2:写 Python 脚本加载翻译创建multi_lang.py,功能是:
提示用户选择语言加载对应的 YAML 翻译文件用翻译内容和用户交互代码(保证能运行):代码语言:python复制import yaml
def load_translation(lang):
"""加载对应语言的翻译文件"""
# 定义语言和文件名的对应关系
lang_files = {
'zh': 'zh.yaml',
'en': 'en.yaml'
}
# 检查语言是否支持
if lang not in lang_files:
return None
# 加载YAML文件
try:
with open(lang_files[lang], 'r', encoding='utf-8') as f:
return yaml.safe_load(f)
except Exception as e:
print(f"加载翻译文件失败:{e}")
return None
def main():
# 先提示选择语言(默认中文)
default_lang = 'zh'
print(f"默认语言是中文,如需英文请输入'en'")
user_lang = input("请输入语言(zh/en):").strip().lower() or default_lang
# 加载翻译
trans = load_translation(user_lang)
if not trans:
print("不支持的语言,使用默认中文!")
trans = load_translation('zh')
# 用翻译内容和用户交互
print("n" + trans['welcome'])
# 这里可以加其他功能,比如显示菜单
input("n按回车键退出...")
print(trans['exit_msg'])
if __name__ == "__main__":
main()怎么运行:把zh.yaml、en.yaml、multi_lang.py放同一个文件夹运行multi_lang.py,输入zh或en试试运行示例(输入 en):代码语言:python复制默认语言是中文,如需英文请输入'en'
请输入语言(zh/en):en
Welcome to the program!
按回车键退出...
Program exited, goodbye!这个场景的好处是:以后要加日语、法语,只需要新建jp.yaml、fr.yaml,不用改 Python 代码,维护起来特别方便。
9. 踩坑指南:常见问题 & 错误解决刚开始用 YAML,很容易踩坑,我整理了几个最常见的问题,告诉你怎么解决。
9.1 缩进错误(最常见!)错误现象:加载时报错yaml.scanner.ScannerError: mapping values are not allowed here,或者expected
错误原因:混用了 Tab 和空格(YAML 只认空格)缩进级别不一致(比如有的地方缩 2 个空格,有的缩 4 个)错误示例:代码语言:python复制# 错误:address下面的province用了Tab缩进,其他用空格
person:
name: 张三
address:
province: 广东 # 这里是Tab,错了!
city: 深圳 # 这里是2个空格解决方法:所有缩进都用空格(推荐 2 个)用文本编辑器开启 “显示空格” 功能(VSCode 按 Ctrl+Shift+P,输 “Toggle Render Whitespace”),能看到是不是混用了 Tab。9.2 键值对格式错误错误现象:报错yaml.parser.ParserError: expected ':'。
错误原因:键和值之间没加空格(比如name:张三,少了空格)漏了冒号(比如name 张三)错误示例:代码语言:python复制# 错误1:冒号后没空格
name:张三
age:25
# 错误2:漏了冒号
name 张三解决方法:严格遵守 “键:值” 格式,冒号后必须加一个空格。
9.3 布尔值和字符串混淆错误现象:YAML 里写的 “yes”,加载后变成了 Python 的True,不是字符串。
原因:YAML 会把yes/no/true/false这些词解析成布尔值,如果你想让它们是字符串,必须加引号。
示例:代码语言:python复制# 会被解析成布尔值True
is_ok: yes
# 正确:加引号,解析成字符串"yes"
is_ok_str: "yes"9.4 中文乱码错误现象:加载 YAML 后,中文显示成乱码(比如'xe5xbcxa0xe4xb8x89')。
原因:open 文件时没指定编码encoding='utf-8'。
解决方法:打开文件时一定要加encoding='utf-8':
代码语言:python复制with open("first.yaml", 'r', encoding='utf-8') as f: # 加encoding
data = yaml.safe_load(f)10. 面试必看:YAML 常见问题 & 回答如果面试 Python 相关岗位,可能会被问到 YAML,这些问题和回答记下来,够用了。
问题 1:YAML 是什么?它有什么优点?回答:YAML 是一种人类可读的数据序列化语言,主要用来存配置文件或结构化数据。优点有:
易读性高,靠缩进表示结构,不用写大括号中括号;支持注释,方便维护;支持多种数据类型,包括基础类型和嵌套的字典、列表;跨语言支持好,Python、Java 这些都能用;适合做配置管理,比如项目参数、多语言翻译。问题 2:Python 中处理 YAML 需要什么库?怎么加载 YAML 文件?回答:需要第三方库pyyaml,先装pip install pyyaml。加载文件用yaml.safe_load(),步骤是:
用open(file_path, 'r', encoding='utf-8')打开文件;用yaml.safe_load(f)加载内容,得到 Python 的字典或列表;注意不能用yaml.load(),因为它不安全,可能执行恶意代码。问题 3:YAML 中的管道符(|)和折叠符(>)有什么区别?回答:都是用来处理多行字符串的,区别在换行的处理:
管道符|:会保留所有换行和空格,写的时候是什么格式,加载后就是什么格式;折叠符>:会把换行转换成空格(最后一个换行保留),适合存连续的文本,不想换行。问题 4:YAML 怎么实现数据重用?回答:用锚点和别名:
用&锚点名定义要重用的数据(比如address: &addr {province: 北京});用*锚点名引用数据(比如user: {name: 张三, addr: *addr});如果想在引用的基础上改数据,可以用<<合并锚点字典,再覆盖键(比如user: {<<: *addr, city: 朝阳})。问题 5:为什么不推荐用 yaml.load ()?生产环境应该用什么?回答:因为yaml.load()默认用Loader解析器,这个解析器能执行 Python 代码,如果加载的 YAML 有恶意代码(比如执行系统命令),会造成安全风险。生产环境必须用yaml.safe_load(),它只会解析基础数据类型,不会执行任何代码,更安全。
问题 6:YAML 和 JSON 有什么区别?什么时候用 YAML,什么时候用 JSON?回答:区别主要在可读性和功能:
可读性:YAML 靠缩进,支持注释,更易读;JSON 靠 {}[],不支持注释,机器解析快但人看费劲;功能:YAML 支持锚点、多行字符串;JSON 不支持;兼容性:JSON 是 YAML 的子集,所有 JSON 文件都能被 YAML 解析,但反过来不行。使用场景:
存配置文件(比如项目参数、多语言):用 YAML,方便维护;接口传输数据(比如前后端交互):用 JSON,因为跨语言兼容性更好,解析速度快。11. 总结:YAML 能帮你做什么?看到这里,你应该明白 YAML 的核心价值了:把数据和代码分开,让配置更灵活、维护更简单。
比如:
项目里的数据库配置、API 地址,不用写在代码里,存在 YAML 里,改配置不用动代码;多语言项目,把翻译内容存在不同的 YAML 文件里,切换语言只需要加载不同文件;测试数据用 YAML 存,写测试用例时直接加载,不用硬编码。YAML 不难,重点是记住缩进规则、安全加载,再掌握锚点、多行字符串这些实用功能,就能轻松搞定 Python 里的配置管理和数据序列化了。
赶紧动手试试吧!把上面的示例代码跑一遍,遇到问题对照 “踩坑指南” 解决,很快就能上手。