输入输出与格式化字符串

程序要想活起来,就得会跟外界说话。这一篇讲 Python 中最基础的输入输出:input()print(),以及新时代最优雅的字符串格式化方式 —— f-string。搞懂它们之后,你就能写出”接收用户输入 → 处理 → 打印结果”的完整脚本了。这看起来简单,但里面有很多”看起来会实际不会”的细节,比如 printsep/end/flush 参数、input 的类型陷阱、f-string 的高级用法等等。

一、print:不止是打印

先看最基础用法:

1
2
3
4
5
print("Hello, World!")
print("咖飞", "3 岁") # 多个参数,默认用空格分隔
print("咖飞", "3 岁", sep=" - ") # 自定义分隔符
print("行 1", end=" | ") # 不换行,用 " | " 结束
print("行 2") # 输出:行 1 | 行 2
  • *objects:要打印的对象(可以任意多个);
  • sep:多个对象之间的分隔符,默认为 ' '
  • end:结束字符,默认为 '\n'
  • file:输出到哪个文件对象,默认是 sys.stdout
  • flush:是否立刻刷新缓冲区,默认 False

输出到文件

1
2
with open("log.txt", "w", encoding="utf-8") as f:
print("日志内容", file=f)

with 会在退出块时自动关闭文件,具体见 with 语句与上下文管理器

输出到 stderr

错误信息应该输出到标准错误流,方便区分正常输出:

1
2
import sys
print("这是错误信息", file=sys.stderr)

flush 的作用

Python 默认是行缓冲(终端)或块缓冲(管道/文件),有时候你会发现输出没立刻显示:

1
2
3
4
5
6
import time

for i in range(5):
print(f"进度 {i}", end=" ")
time.sleep(1)
# 5 秒后一次性打印,因为没换行触发刷新

flush=True 就实时了:

1
2
3
for i in range(5):
print(f"进度 {i}", end=" ", flush=True)
time.sleep(1)

打印任意对象

print 会自动调用对象的 __str__ 方法把它转成字符串:

1
2
3
4
print([1, 2, 3])         # [1, 2, 3]
print({"a": 1, "b": 2}) # {'a': 1, 'b': 2}
print((1, 2)) # (1, 2)
print(None) # None

想调试对象内部结构时,repr 更详细:

1
2
3
s = "咖飞"
print(s) # 咖飞
print(repr(s)) # '咖飞' 带引号,能看出是字符串

调试大型嵌套数据用 pprint

1
2
3
from pprint import pprint
data = {"users": [{"name": "a"}, {"name": "b"}]}
pprint(data, width=40)

二、input:读取用户输入

1
2
name = input("请输入你的名字:")
print(f"你好,{name}")

三条关键规则:

  1. input() 永远返回字符串,即便你输入了数字;
  2. 输入的末尾不含换行符
  3. 参数是”提示语”(prompt),显示在光标前。

转类型

想要数字必须显式转换:

1
2
age = int(input("年龄:"))         # 输入 abc 会抛 ValueError
weight = float(input("体重:"))

永远要考虑用户会瞎输入

1
2
3
4
5
6
7
8
9
10
def read_int(prompt: str) -> int:
"""反复让用户输入直到输入合法整数。"""
while True:
raw = input(prompt).strip()
try:
return int(raw)
except ValueError:
print("请输入合法整数")

age = read_int("年龄:")

一次性读多个值

1
2
3
4
5
6
7
raw = input("请输入身高体重(用空格分隔):")   # 例:170 65
parts = raw.split()
if len(parts) != 2:
print("格式错误")
else:
height, weight = map(float, parts)
print(f"BMI: {weight / (height/100)**2:.2f}")

getpass:隐藏密码

密码不应该显示在终端上:

1
2
from getpass import getpass
pwd = getpass("请输入密码:") # 输入时不显示

三、f-string:格式化的正确姿势

f-string 是 Python 3.6+ 引入的字符串前缀 f,可以在花括号里直接嵌入表达式:

1
2
3
4
5
6
7
8
9
10
name = "咖飞"
age = 3
print(f"我叫 {name},今年 {age} 岁")

# 表达式也可以
print(f"明年 {age + 1} 岁")
print(f"名字长度 {len(name)}")

# 调用方法
print(f"大写:{name.upper()}")

格式说明符 :...

1
2
3
4
5
6
7
pi = 3.14159265
print(f"{pi:.2f}") # 3.14 保留 2 位小数
print(f"{pi:10.2f}") # ' 3.14' 总宽度 10,右对齐
print(f"{pi:<10.2f}") # '3.14 ' 左对齐
print(f"{pi:^10.2f}") # ' 3.14 ' 居中
print(f"{pi:>10.2f}") # ' 3.14' 右对齐(默认)
print(f"{pi:0>10.2f}") # '0000003.14' 填充 0

千分位

1
2
3
big = 1234567890
print(f"{big:,}") # 1,234,567,890 千分位分隔
print(f"{big:_}") # 1_234_567_890 下划线分隔

百分比

1
2
rate = 0.256
print(f"{rate:.1%}") # 25.6% 自动 *100 加 %

进制转换

1
2
3
4
5
6
7
n = 255
print(f"{n:b}") # 11111111 二进制
print(f"{n:o}") # 377 八进制
print(f"{n:x}") # ff 十六进制小写
print(f"{n:X}") # FF 十六进制大写
print(f"{n:#x}") # 0xff 带前缀
print(f"{n:08b}") # 11111111 补零到 8 位

日期时间

1
2
3
from datetime import datetime
now = datetime.now()
print(f"{now:%Y-%m-%d %H:%M:%S}") # 2025-09-05 09:12:38

调试神器 =(Python 3.8+)

1
2
3
4
x = 42
y = 10
print(f"{x=}, {y=}, {x*y=}")
# x=42, y=10, x*y=420

以前调试要写 print("x =", x),现在 f"{x=}" 一键搞定,是最实用的现代 Python 特性之一。

多行 f-string

1
2
3
4
5
6
7
name = "咖飞"
report = f"""
姓名:{name}
年龄:{3}
状态:{"在学习中" if True else "偷懒"}
"""
print(report)

花括号本身

想输出字面的 {},写两个:

1
2
print(f"{{}}")       # {}
print(f"字典字面:{{'a': 1}}") # 字典字面:{'a': 1}

四、格式化的历史三代

作为对比,看看 Python 里字符串格式化的演变:

1
2
3
4
5
6
7
8
9
10
11
name, age = "咖飞", 3

# 第 1 代:% (C printf 风格,Python 2 时代)
print("我叫 %s,%d 岁" % (name, age))

# 第 2 代:str.format() (Python 2.6+)
print("我叫 {},{} 岁".format(name, age))
print("我叫 {n},{a} 岁".format(n=name, a=age))

# 第 3 代:f-string (Python 3.6+) ← 推荐
print(f"我叫 {name}{age} 岁")

新代码请用 f-string。看到 %format() 一般是老代码。有一个例外:当格式串本身来自用户或配置文件时,就必须用 format(),因为 f-string 是编译期解析变量的。

1
2
3
# 从配置里读的模板
template = "Hello, {name}! Today is {date}"
print(template.format(name="咖飞", date="2025-09-05"))

五、一个综合小项目:ATM 提款机

我们把这一篇学到的东西糅到一起:

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# atm.py
"""极简 ATM 提款机演示 input/print/f-string 用法。"""

from getpass import getpass


def prompt_amount() -> float:
while True:
raw = input("请输入提款金额(元):").strip()
try:
amount = float(raw)
except ValueError:
print("请输入数字")
continue
if amount <= 0:
print("金额必须大于 0")
continue
if amount % 100 != 0:
print("金额必须是 100 的整数倍")
continue
return amount


def main():
balance = 8888.88
pwd = getpass("请输入 6 位密码:")
if pwd != "123456":
print("密码错误,卡已吞", flush=True)
return

print(f"欢迎,当前余额:¥{balance:,.2f}")
amount = prompt_amount()
if amount > balance:
print(f"余额不足!你想取 ¥{amount:,.2f},但只有 ¥{balance:,.2f}")
return

balance -= amount
print("=" * 30)
print(f"取款成功:¥{amount:,.2f}")
print(f"当前余额:¥{balance:,.2f}")
print(f"时间:{__import__('datetime').datetime.now():%Y-%m-%d %H:%M:%S}")
print("=" * 30)


if __name__ == "__main__":
main()

这段代码用到了:

  • input() 与循环校验;
  • getpass() 隐藏密码;
  • f-string 的多种格式(千分位、小数位、日期、宽度对齐);
  • print(sep, end, flush)
  • 函数(下一部分要讲)。

六、命令行参数(简版)

如果不想让程序停下来等 input(),而是直接在命令行传参:

1
2
3
# hello.py
import sys
print(f"参数:{sys.argv}")

运行:

1
2
python hello.py 咖飞 3
# 参数:['hello.py', '咖飞', '3']

更强大的命令行解析用 argparse(标准库)或 typer/click(第三方),我们后面单独讲。

七、常见陷阱

  1. input() 结果是字符串age = input()age + 1 报错,要 int(age) + 1
  2. print 中的 , 是分隔多个对象,不是字符串拼接。print("a", "b") 输出 a b,不是 ab。要拼接用 f-string 或 +
  3. f-string 里的引号f"{name.replace("a", "b")}" 在 Python 3.11 前会报错(引号打架)。用双引号包外层、单引号包里层,或反过来。
  4. flush 忘记加:进度条、心跳输出不显示,绝大多数是没加 flush=True
  5. 中文 Windows 终端乱码chcp 65001 切换 UTF-8,或者用 print(..., flush=True) 后再重定向。
  6. input 里的换行input() 不含结尾换行,如果需要按空行退出可以判断 if not line:
  7. f-string 不能格式化字典的引号 keyf"{d["key"]}" 引号冲突,改成 d['key']

八、小结与延伸阅读

  • printsep/end/file/flush 四个宝藏参数;
  • input() 只返回字符串,用完记得转类型;
  • 密码输入用 getpass
  • 新代码统一用 f-string,{value:.2f}{n:,}{now:%Y-%m-%d} 记熟;
  • 调试小技巧:f"{x=}"
  • 模板来自外部数据时用 str.format()
  • 命令行参数入门用 sys.argv,进阶用 argparse。

延伸阅读:

下一篇 条件语句 if/elif/else 我们要开始让程序做”选择”了。