04-输入与输出
本章代码基于 Python 3.11+ 编写
输入输出是程序与用户交互的基本方式,掌握 I/O 让程序能够接收数据并展示结果。
本章讲解 print() 输出函数、input() 输入函数、字符串格式化以及文件读写的基础知识。
概念铺垫
为什么需要输入输出?一个真实的交互场景
问题场景: 你在开发一个用户登录程序,需要获取用户输入的用户名和密码,并显示登录结果。
没有输入输出的困惑:
- 如何让程序接收用户输入的信息?
- 如何将处理结果展示给用户?
- 如何控制输出的格式让信息更清晰?
使用输入输出的解决方案:
python
# 获取用户输入
username: str = input("请输入用户名:")
password: str = input("请输入密码:")
# 处理并输出结果
if username == "admin" and password == "123456":
print(f"欢迎,{username}!登录成功。")
else:
print("用户名或密码错误,登录失败。")这就是输入输出的价值:让程序能够与人交流。
输入输出解决了什么问题?
输入输出的本质是:程序与外部世界交换数据的通道。
就像你用眼睛看(输入)、用嘴巴说(输出),程序也需要"看"和"说"。
输入输出的类型:
- 控制台 I/O:通过终端接收键盘输入和显示文本输出
- 文件 I/O:从文件读取数据和将数据写入文件
- 网络 I/O:通过网络接收和发送数据(后续章节讲解)
输出的最简用法
最常用的输出函数只有 print()。
python
# 输出文本
print("Hello, Python!")
# 输出变量
name: str = "张三"
print(name)
# 输出多个值
age: int = 18
print("姓名:", name, "年龄:", age)
# 格式化输出
print(f"姓名:{name},年龄:{age}")这就是输出的基本用法。接下来我们详细学习各类输出方式。
L1 理解层:会用
print() 函数详解
基本语法
┌─────────────────────────────────────────────────────────────┐
│ print() 函数语法 │
├─────────────────────────────────────────────────────────────┤
│ │
│ print(value, ..., sep=' ', end='\n', file=sys.stdout, │
│ flush=False) │
│ │
│ 参数说明: │
│ • value 要输出的值(可以多个,用逗号分隔) │
│ • sep 多个值之间的分隔符(默认空格) │
│ • end 输出结尾(默认换行符 \n) │
│ • file 输出目标(默认标准输出/屏幕) │
│ • flush 是否立即刷新缓冲区(默认 False) │
│ │
└─────────────────────────────────────────────────────────────┘最简示例
python
print("Hello, World!") # 输出:Hello, World!控制输出格式
sep 参数:自定义分隔符
┌─────────────────────────────────────────────────────────────┐
│ sep 参数效果 │
├─────────────────────────────────────────────────────────────┤
│ │
│ print("A", "B", "C") → A B C (默认空格) │
│ print("A", "B", "C", sep="") → ABC (无分隔) │
│ print("A", "B", "C", sep="-") → A-B-C (自定义分隔符) │
│ print("A", "B", "C", sep=", ") → A, B, C (逗号+空格) │
│ │
└─────────────────────────────────────────────────────────────┘示例
python
# 默认分隔符(空格)
print("姓名", "年龄", "城市") # 姓名 年龄 城市
# 使用制表符分隔
print("姓名", "年龄", "城市", sep="\t") # 姓名 年龄 城市
# 使用自定义分隔符
print("2024", "01", "15", sep="-") # 2024-01-15
print("user", "example.com", sep="@") # user@example.comend 参数:控制行尾
┌─────────────────────────────────────────────────────────────┐
│ end 参数效果 │
├─────────────────────────────────────────────────────────────┤
│ │
│ print("Hello") → Hello\n (默认换行) │
│ print("Hello", end="") → Hello (不换行) │
│ print("Hello", end=" ") → Hello (空格结尾) │
│ print("Hello", end="---") → Hello--- (自定义结尾) │
│ │
└─────────────────────────────────────────────────────────────┘示例
python
# 默认:每次输出后换行
print("第一行")
print("第二行")
# 输出:
# 第一行
# 第二行
# 不换行输出
print("第一行", end=" ")
print("第二行")
# 输出:第一行 第二行
# 进度条效果
import time
for i in range(5):
print(f"加载 {i*20}%", end="\r")
time.sleep(0.5)
print("\n加载完成!")file 参数:输出到文件
python
# 输出到文件
with open("output.txt", "w", encoding="utf-8") as f:
print("这是写入文件的内容", file=f)
# 输出到标准错误
import sys
print("错误信息", file=sys.stderr)字符串格式化(核心)
Python 提供多种字符串格式化方式,推荐掌握 f-string(最现代、最简洁)。
┌─────────────────────────────────────────────────────────────┐
│ Python 字符串格式化方式对比 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 方式 版本要求 推荐度 示例 │
│ ────────────────────────────────────────────────────── │
│ % 格式化 所有版本 ⭐ "Hi %s" % name │
│ str.format() Python 2.6+ ⭐⭐ "Hi {}".format() │
│ f-string Python 3.6+ ⭐⭐⭐⭐ f"Hi {name}" │
│ │
│ ⭐ 推荐:使用 f-string(Python 3.6+) │
│ │
└─────────────────────────────────────────────────────────────┘方式一:f-string(推荐)
f-string: 在字符串前加 f,用 {} 嵌入变量或表达式。
f-string 结构:
┌─────────────────────────────────────────────────────────────┐
│ f"文本 {变量} 文本 {表达式} 文本" │
│ │ │ │ │ │
│ │ │ │ └─ 可以是任意表达式 │
│ │ │ └─ 嵌入代码的位置 │
│ │ └─ 变量名或表达式 │
│ └─ f 或 F 前缀(必须) │
│ │
│ 示例: │
│ name = "张三" │
│ age = 18 │
│ f"姓名:{name},年龄:{age}" │
│ → "姓名:张三,年龄:18" │
│ │
│ f"明年年龄:{age + 1}" │
│ → "明年年龄:19" │
└─────────────────────────────────────────────────────────────┘最简示例
python
name: str = "张三"
age: int = 18
print(f"姓名:{name},年龄:{age}")
# 输出:姓名:张三,年龄:18关键代码解释
| 代码 | 含义 | 结果 |
|---|---|---|
f"姓名:{name}" | 嵌入变量 | "姓名:张三" |
f"明年:{age + 1}" | 嵌入表达式 | "明年:19" |
f"大写:{name.upper()}" | 调用方法 | "大写:张三" |
详细示例
python
name: str = "张三"
score: float = 85.5
# 嵌入变量
print(f"你好,{name}!")
# 嵌入表达式
print(f"你的分数是:{score}")
print(f"分数加倍:{score * 2}")
# 调用方法
print(f"名字大写:{name.upper()}")
# 多行 f-string
message: str = f"""
=== 学生信息 ===
姓名:{name}
分数:{score}
状态:{'通过' if score >= 60 else '未通过'}
"""
print(message)格式化数值
┌─────────────────────────────────────────────────────────────┐
│ f-string 数值格式化格式 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 格式 含义 示例 │
│ ────────────────────────────────────────────────────── │
│ {value:.2f} 保留 2 位小数 3.14159 → 3.14 │
│ {value:.0f} 整数形式 3.14 → 3 │
│ {value:,.2f} 千位分隔符 1234567.89 → │
│ 1,234,567.89 │
│ {value:.2%} 百分比 0.856 → 85.60% │
│ {value:.2e} 科学计数法 12345 → 1.23e+04 │
│ {value:<10} 左对齐,宽 10 位 'Python' → 'Python '│
│ {value:>10} 右对齐,宽 10 位 'Python' → ' Python'│
│ {value:^10} 居中,宽 10 位 'Python' → ' Python '│
│ {value:0>5} 右对齐,0 填充 42 → 00042 │
│ │
└─────────────────────────────────────────────────────────────┘示例
python
price: float = 1234.567
discount: float = 0.85
population: int = 1400000000
# 小数位数
print(f"价格:{price:.2f}") # 价格:1234.57
# 百分比
print(f"折扣:{discount:.1%}") # 折扣:85.0%
# 千位分隔符
print(f"人口:{population:,}") # 人口:1,400,000,000
# 对齐输出
print(f"{'商品':<10} | {'价格':>8}")
print(f"{'Python教程':<10} | {59.9:>8.2f}")
print(f"{'键盘':<10} | {199.0:>8.2f}")
# 补零
for i in range(1, 6):
print(f"第 {i:02d} 题")
# 输出:第 01 题, 第 02 题, ...方式二:str.format()
str.format(): 使用 {} 占位,通过 .format() 方法填充。
python
name: str = "张三"
age: int = 18
# 基本用法
print("姓名:{},年龄:{}".format(name, age))
# 索引占位
print("姓名:{0},年龄:{1}".format(name, age))
print("年龄:{1},姓名:{0}".format(name, age)) # 交换顺序
# 命名占位
print("姓名:{n},年龄:{a}".format(n=name, a=age))
# 格式化数值
print("价格:{:.2f}".format(3.14159))方式三:% 格式化(旧式)
% 格式化: 类似 C 语言的 printf 风格。
python
name: str = "张三"
age: int = 18
score: float = 85.5
# 基本用法
print("姓名:%s,年龄:%d" % (name, age))
# 格式化数值
print("分数:%.1f" % score) # 分数:85.5
# 常见占位符
print("字符串:%s" % "Hello") # %s 字符串
print("整数:%d" % 42) # %d 整数
print("浮点数:%.2f" % 3.14) # %f 浮点数提示: % 格式化是旧式语法,新项目推荐使用 f-string。
input() 函数详解
基本语法
┌─────────────────────────────────────────────────────────────┐
│ input() 函数语法 │
├─────────────────────────────────────────────────────────────┤
│ │
│ input(prompt=None) │
│ │
│ 参数说明: │
│ • prompt 提示文本(可选,显示在输入前) │
│ │
│ 返回值: │
│ • 始终返回字符串类型(str) │
│ │
│ 执行流程: │
│ 1. 显示提示文本(如果有) │
│ 2. 等待用户输入 │
│ 3. 用户按回车键 │
│ 4. 返回输入的文本 │
│ │
└─────────────────────────────────────────────────────────────┘最简示例
python
# 基本输入
name: str = input("请输入你的名字:")
print(f"你好,{name}!")关键代码解释
| 代码 | 含义 | 结果 |
|---|---|---|
input() | 等待用户输入 | 返回输入的字符串 |
input("提示:") | 显示提示后等待 | 返回输入的字符串 |
int(input()) | 输入转整数 | 将字符串转为整数 |
详细示例
python
# 获取用户信息
name: str = input("请输入姓名:")
age_str: str = input("请输入年龄:")
# 类型转换(input 返回的总是字符串)
age: int = int(age_str)
print(f"{name},明年你 {age + 1} 岁")输入类型转换
┌─────────────────────────────────────────────────────────────┐
│ 输入类型转换注意事项 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ⚠️ 重要:input() 始终返回字符串! │
│ │
│ 需要其他类型时,必须手动转换: │
│ │
│ int(input()) → 转换为整数 │
│ float(input()) → 转换为浮点数 │
│ bool(input()) → ⚠️ 陷阱!非空字符串都是 True │
│ │
│ ❌ 错误: │
│ age = input("年龄:") # age 是字符串 "18" │
│ print(age + 1) # TypeError! │
│ │
│ ✅ 正确: │
│ age = int(input("年龄:")) # age 是整数 18 │
│ print(age + 1) # 19 │
│ │
└─────────────────────────────────────────────────────────────┘示例
python
# 整数输入
age: int = int(input("请输入年龄:"))
# 浮点数输入
height: float = float(input("请输入身高(米):"))
# 多个输入(空格分隔)
data: str = input("输入两个数字(空格分隔):")
parts: list[str] = data.split()
x: float = float(parts[0])
y: float = float(parts[1])
print(f"和:{x + y}")安全的输入处理
处理无效输入
python
# ❌ 不安全的做法:直接转换,可能崩溃
age: int = int(input("请输入年龄:")) # 输入 "abc" 会报错!
# ✅ 安全的做法:使用 try-except
while True:
try:
age: int = int(input("请输入年龄:"))
break
except ValueError:
print("输入无效,请输入一个整数!")
print(f"你的年龄是:{age}")输入验证函数
python
def get_int(prompt: str, min_val: int | None = None, max_val: int | None = None) -> int:
"""
获取用户输入的整数,可选范围验证
Args:
prompt: 提示文本
min_val: 最小值(可选)
max_val: 最大值(可选)
Returns:
有效的整数输入
"""
while True:
try:
value: int = int(input(prompt))
if min_val is not None and value < min_val:
print(f"输入不能小于 {min_val}")
continue
if max_val is not None and value > max_val:
print(f"输入不能大于 {max_val}")
continue
return value
except ValueError:
print("请输入有效的整数!")
# 使用示例
age: int = get_int("请输入年龄(0-150):", min_val=0, max_val=150)
print(f"年龄验证通过:{age}")从简单到复杂:输入输出的渐进应用
层级1:基础输出
python
# 简单输出
print("Hello, Python!")
# 输出变量
message: str = "欢迎学习 Python"
print(message)层级2:格式化输出
python
# f-string 格式化
name: str = "张三"
score: float = 95.5
print(f"学生:{name},分数:{score:.1f}")层级3:基础输入
python
# 获取输入并处理
user_name: str = input("请输入用户名:")
print(f"欢迎,{user_name}!")层级4:输入类型转换
python
# 输入数字并计算
num1: float = float(input("输入第一个数:"))
num2: float = float(input("输入第二个数:"))
print(f"和:{num1 + num2}")
print(f"积:{num1 * num2}")层级5:安全输入与格式化表格
python
# 安全的输入 + 格式化输出表格
def get_student_info() -> dict[str, str | float]:
"""获取学生信息"""
while True:
try:
name: str = input("姓名:")
if not name.strip():
print("姓名不能为空")
continue
score: float = float(input("分数:"))
if not 0 <= score <= 100:
print("分数必须在 0-100 之间")
continue
return {"name": name, "score": score}
except ValueError:
print("请输入有效数字!")
# 收集数据
students: list[dict[str, str | float]] = []
for _ in range(2):
students.append(get_student_info())
# 格式化输出表格
print(f"\n{'姓名':<10} | {'分数':>6} | {'等级':>4}")
print("-" * 26)
for s in students:
score: float = float(s["score"])
grade: str = "优秀" if score >= 90 else "良好" if score >= 70 else "及格" if score >= 60 else "不及格"
print(f"{s['name']:<10} | {score:>6.1f} | {grade:>4}")关键代码解释:
| 代码 | 含义 | 为什么这样写 |
|---|---|---|
while True: try: ... except ValueError: | 无限循环+异常捕获:持续要求输入直到合法 | 用户输错不崩溃,捕获后提示重新输入 |
if not name.strip(): | 判断去除空白后是否为空字符串 | 防止用户只按空格通过验证 |
if not 0 <= score <= 100: | Python 链式比较:等价于 0 <= score and score <= 100 | 范围验证,比写两个条件更简洁 |
for _ in range(2): | 循环 2 次,_ 表示不使用循环变量 | 约定俗成:_ 表示该变量值不重要 |
f"{'姓名':<10}" | 字符串左对齐,总宽度 10 | 固定列宽使表格整齐对齐 |
"优秀" if score >= 90 else "良好" if score >= 70 else ... | 链式嵌套三元表达式:多档判断 | 简洁写法,注意从高分到低分依次判断 |
综合应用:简易计算器
这个示例综合运用输入输出:
python
# 简易计算器(Python 3.11+)
def main() -> None:
"""简易计算器主程序"""
print("=== 简易计算器 ===")
print("支持运算:+ - * /")
print("输入 'q' 退出程序\n")
while True:
# 获取输入
user_input: str = input("\n请输入算式(如 3 + 5):").strip()
# 退出检查
if user_input.lower() == 'q':
print("感谢使用,再见!")
break
# 解析算式
try:
parts: list[str] = user_input.split()
if len(parts) != 3:
print("格式错误!请按 '数字 运算符 数字' 格式输入")
continue
num1: float = float(parts[0])
operator: str = parts[1]
num2: float = float(parts[2])
# 计算结果
if operator == '+':
result: float = num1 + num2
elif operator == '-':
result = num1 - num2
elif operator == '*':
result = num1 * num2
elif operator == '/':
if num2 == 0:
print("错误:除数不能为零!")
continue
result = num1 / num2
else:
print(f"错误:不支持的运算符 '{operator}'")
continue
# 输出结果
print(f"结果:{num1:g} {operator} {num2:g} = {result:g}")
except ValueError:
print("错误:请输入有效的数字!")
if __name__ == "__main__":
main()这个程序展示了:
print()输出提示和结果input()获取用户输入- 字符串解析和类型转换
- 错误处理和输入验证
- f-string 格式化输出
- 循环交互设计
关键代码说明:
| 代码 | 含义 | 为什么这样写 |
|---|---|---|
input(...).strip() | 获取输入并去除两端空白 | 用户可能误输入前后空格,strip 防止解析出错 |
user_input.split() | 按空白字符分割成列表 | 将 "3 + 5" 拆为 ["3", "+", "5"] 三部分 |
float(parts[0]) | 将字符串转为浮点数 | input 返回字符串,必须转换才能计算 |
except ValueError: | 捕获类型转换失败异常 | 用户输入非数字(如 "abc")时 float() 抛出此异常 |
continue | 跳过本次循环,回到 while 起点 | 出错后不中断程序,让用户重新输入 |
{num1:g} | 格式化数字,去除多余的零 | 3.0 显示为 3,3.50 显示为 3.5,更整洁 |
文件读写基础
写入文件
python
# 方式1:使用 print 写入
with open("hello.txt", "w", encoding="utf-8") as f:
print("Hello, File!", file=f)
# 方式2:使用 write 方法
with open("hello.txt", "w", encoding="utf-8") as f:
f.write("Hello, File!\n")
f.write("第二行内容\n")读取文件
python
# 读取全部内容
with open("hello.txt", "r", encoding="utf-8") as f:
content: str = f.read()
print(content)
# 逐行读取
with open("hello.txt", "r", encoding="utf-8") as f:
for line in f:
print(line.strip()) # strip() 去除行尾换行符
# 读取所有行到列表
with open("hello.txt", "r", encoding="utf-8") as f:
lines: list[str] = f.readlines()
print(lines)文件模式
┌─────────────────────────────────────────────────────────────┐
│ 文件打开模式 │
├──────────┬──────────────────────────────────────────────────┤
│ 模式 │ 说明 │
├──────────┼──────────────────────────────────────────────────┤
│ "r" │ 只读(默认),文件不存在会报错 │
│ "w" │ 只写,覆盖原内容,文件不存在会创建 │
│ "a" │ 追加,在文件末尾添加,文件不存在会创建 │
│ "x" │ 独占创建,文件已存在会报错 │
│ "rb" │ 二进制只读 │
│ "wb" │ 二进制只写 │
└──────────┴──────────────────────────────────────────────────┘L2 实践层:用好
推荐做法
| 做法 | 原因 | 示例 |
|---|---|---|
| 新项目统一用 f-string | 最简洁、最快速、最易读 | f"姓名:{name}" |
文件操作用 with open() | 自动关闭文件,防资源泄漏 | with open("a.txt") as f: |
| 输入后立即做类型转换 | 避免字符串运算错误 | int(input("年龄:")) |
用 try-except 包裹输入转换 | 防止无效输入导致崩溃 | try: int(input()) except ValueError: |
| 大数据用逐行读取 | 避免一次性加载到内存 | for line in f: |
输出重定向用 file= 参数 | 灵活切换输出目标 | print(err, file=sys.stderr) |
进度条用 end="\r" 覆盖 | 原地刷新,视觉友好 | print(f"{p}%", end="\r") |
表格输出用 :<n / :>n 对齐 | 固定列宽,格式整齐 | f"{'名称':<10} {价格:>8}" |
反模式:不要这样做
python
# ❌ 错误:新项目使用 % 格式化
print("姓名:%s,年龄:%d" % (name, age))
# ✅ 正确:使用 f-string
print(f"姓名:{name},年龄:{age}")python
# ❌ 错误:不处理无效输入
age: int = int(input("年龄:")) # 输入 "abc" 崩溃
# ✅ 正确:用 try-except 安全处理
while True:
try:
age: int = int(input("年龄:"))
break
except ValueError:
print("请输入整数!")python
# ❌ 错误:忘记关闭文件(资源泄漏)
f = open("data.txt", "r")
content = f.read()
# f.close() # 忘记关闭!
# ✅ 正确:使用 with 自动管理
with open("data.txt", "r") as f:
content = f.read()python
# ❌ 错误:用 f-string 输出大量调试信息却没有格式控制
data = {"key1": "val1", "key2": "val2"} # 复杂对象
print(f"数据:{data}") # 可能难以阅读
# ✅ 正确:用 pprint 或 json 美化输出
import json
print(json.dumps(data, indent=2, ensure_ascii=False))python
# ❌ 错误:一次性读取大文件
with open("large.log") as f:
lines = f.readlines() # 10GB 文件直接崩溃
# ✅ 正确:逐行读取
with open("large.log") as f:
for line in f:
process(line)常见陷阱
| 陷阱 | 说明 | 解决方案 |
|---|---|---|
bool(input()) 总是 True | 非空字符串都是 True | 用 input().lower() in ("yes", "y") |
\r 不刷新 | end="\r" 后内容不立即显示 | 加 flush=True |
print() 换行不当 | end="" 缺乏换行导致输出粘连 | 适时补充 print() 换行 |
| 文件编码问题 | Windows 默认 GBK,不是 UTF-8 | 始终指定 encoding="utf-8" |
input() 阻塞 | 等待用户输入时程序卡住 | 考虑超时或异步方案(进阶) |
| f-string 中的引号冲突 | f"name is {d["key"]}" 语法错误 | 用不同引号:f"name is {d['key']}" |
适用场景
| 场景 | 是否推荐 | 原因 |
|---|---|---|
| 简单输出 | ✅ print() | 最基础的输出方式 |
| 变量/表达式嵌入 | ✅ f-string | 现代语法,最简洁 |
| 复杂模板(邮件、报告) | ❓ str.format() 或模板引擎 | f-string 适合短文本 |
| 多语言/国际化 | ❓ 模板引擎(Jinja2 等) | f-string 不便于翻译管理 |
| 调试输出 | ✅ print() + f-string | 快速调试 |
| 生产日志 | ❌ 不推荐 print() | 用 logging 模块(后续章节) |
| 大文件处理 | ✅ 逐行迭代 | 内存友好 |
| 配置文件读写 | ✅ json/toml 模块 | 结构化数据优于纯文本 |
| 敏感信息输入 | ❌ input() 不够 | 用 getpass.getpass() 隐藏输入 |
本章小结
┌─────────────────────────────────────────────────────────────┐
│ 输入与输出 知识要点 │
├─────────────────────────────────────────────────────────────┤
│ │
│ print() 输出: │
│ ✓ 基本用法:print(value) │
│ ✓ sep 参数:控制分隔符(默认空格) │
│ ✓ end 参数:控制行尾(默认换行) │
│ ✓ file 参数:输出到文件 │
│ │
│ 字符串格式化: │
│ ✓ f-string(推荐):f"文本 {变量}" │
│ ✓ 数值格式:{value:.2f} {value:,} {value:.1%} │
│ ✓ 对齐:{value:<10} {value:>10} {value:^10} │
│ │
│ input() 输入: │
│ ✓ 始终返回字符串 │
│ ✓ 需要其他类型时手动转换 │
│ ✓ 使用 try-except 处理无效输入 │
│ │
│ 文件读写: │
│ ✓ 使用 with open() 自动管理文件 │
│ ✓ 模式:r(读)、w(写)、a(追加) │
│ ✓ read() 读全部、readlines() 读行列表 │
│ │
└─────────────────────────────────────────────────────────────┘