程序要想活起来,就得会跟外界说话。这一篇讲 Python 中最基础的输入输出:input()、print(),以及新时代最优雅的字符串格式化方式 —— f-string。搞懂它们之后,你就能写出”接收用户输入 → 处理 → 打印结果”的完整脚本了。这看起来简单,但里面有很多”看起来会实际不会”的细节,比如 print 的 sep/end/flush 参数、input 的类型陷阱、f-string 的高级用法等等。
一、print:不止是打印
先看最基础用法:
1 2 3 4 5
| print("Hello, World!") print("咖飞", "3 岁") print("咖飞", "3 岁", sep=" - ") print("行 1", end=" | ") print("行 2")
|
print 的四个关键参数
*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)
|
加 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]) print({"a": 1, "b": 2}) print((1, 2)) print(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)
|
1 2
| name = input("请输入你的名字:") print(f"你好,{name}")
|
三条关键规则:
input() 永远返回字符串,即便你输入了数字;
- 输入的末尾不含换行符;
- 参数是”提示语”(prompt),显示在光标前。
转类型
想要数字必须显式转换:
1 2
| age = int(input("年龄:")) 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("请输入身高体重(用空格分隔):") 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}") print(f"{pi:10.2f}") print(f"{pi:<10.2f}") print(f"{pi:^10.2f}") print(f"{pi:>10.2f}") print(f"{pi:0>10.2f}")
|
千分位
1 2 3
| big = 1234567890 print(f"{big:,}") print(f"{big:_}")
|
百分比
1 2
| rate = 0.256 print(f"{rate:.1%}")
|
进制转换
1 2 3 4 5 6 7
| n = 255 print(f"{n:b}") print(f"{n:o}") print(f"{n:x}") print(f"{n:X}") print(f"{n:#x}") print(f"{n:08b}")
|
日期时间
1 2 3
| from datetime import datetime now = datetime.now() print(f"{now:%Y-%m-%d %H:%M:%S}")
|
调试神器 =(Python 3.8+)
1 2 3 4
| x = 42 y = 10 print(f"{x=}, {y=}, {x*y=}")
|
以前调试要写 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}}")
|
四、格式化的历史三代
作为对比,看看 Python 里字符串格式化的演变:
1 2 3 4 5 6 7 8 9 10 11
| name, age = "咖飞", 3
print("我叫 %s,%d 岁" % (name, age))
print("我叫 {},{} 岁".format(name, age)) print("我叫 {n},{a} 岁".format(n=name, a=age))
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 提款机演示 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
| import sys print(f"参数:{sys.argv}")
|
运行:
更强大的命令行解析用 argparse(标准库)或 typer/click(第三方),我们后面单独讲。
七、常见陷阱
input() 结果是字符串:age = input() 后 age + 1 报错,要 int(age) + 1。
print 中的 , 是分隔多个对象,不是字符串拼接。print("a", "b") 输出 a b,不是 ab。要拼接用 f-string 或 +。
- f-string 里的引号:
f"{name.replace("a", "b")}" 在 Python 3.11 前会报错(引号打架)。用双引号包外层、单引号包里层,或反过来。
- flush 忘记加:进度条、心跳输出不显示,绝大多数是没加
flush=True。
- 中文 Windows 终端乱码:
chcp 65001 切换 UTF-8,或者用 print(..., flush=True) 后再重定向。
- input 里的换行:
input() 不含结尾换行,如果需要按空行退出可以判断 if not line:。
- f-string 不能格式化字典的引号 key:
f"{d["key"]}" 引号冲突,改成 d['key']。
八、小结与延伸阅读
print 有 sep/end/file/flush 四个宝藏参数;
input() 只返回字符串,用完记得转类型;
- 密码输入用
getpass;
- 新代码统一用 f-string,
{value:.2f}、{n:,}、{now:%Y-%m-%d} 记熟;
- 调试小技巧:
f"{x=}";
- 模板来自外部数据时用
str.format();
- 命令行参数入门用
sys.argv,进阶用 argparse。
延伸阅读:
下一篇 条件语句 if/elif/else 我们要开始让程序做”选择”了。