文章
合集Python 语言基础第 2 / 21 篇

Python 简单输入输出:input() 与 print()

一、input() 不要信任用户的输入

基础语法

user_input = input("请输入: ")  # 返回值永远是字符串(str)

业务实战技巧

1. 防御性类型转换(带验证循环)

生产环境直接转换不安全,需要处理异常:

def get_int(prompt, min_val=None, max_val=None):
    """获取整数输入,带范围验证"""
    while True:
        try:
            value = 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 = get_int("请输入年龄: ", min_val=0, max_val=150)

2. 敏感信息输入(密码隐藏)

业务中输入密码不要用 input,会被历史记录:

import getpass

password = getpass.getpass("请输入密码: ")  # 输入时不显示字符

3. 多行输入与 EOF 处理

处理配置文件粘贴或管道输入:

print("请输入多行文本,Ctrl+D (Unix) 或 Ctrl+Z (Windows) 结束:")
lines = []
try:
    while True:
        line = input()
        lines.append(line)
except EOFError:  # 捕获结束符
    pass
content = "\n".join(lines)

4. 输入超时(自动化场景)

在无人值守或自动化脚本中,防止无限等待:

import signal

class TimeoutError(Exception):
    pass

def timeout_handler(signum, frame):
    raise TimeoutError("输入超时")

# 设置 5 秒超时(Unix/Linux 适用)
signal.signal(signal.SIGALRM, timeout_handler)
signal.alarm(5)

try:
    answer = input("请在 5 秒内输入: ")
    signal.alarm(0)  # 取消超时
except TimeoutError:
    print("\n超时,使用默认值")
    answer = "default"

5. 历史记录与自动补全(CLI 工具)

对于内部管理后台或命令行工具,提升体验:

import readline  # Unix/Linux 下启用输入历史(按上下键)

# 启用后,用户可以使用上下箭头浏览历史输入
name = input("用户名: ")

永远不要 eval(input())

# 危险!用户输入 __import__('os').system('rm -rf /') 就完蛋了
result = eval(input("请输入表达式: "))  # 禁止!

# 安全替代:使用 ast.literal_eval 处理字面量
import ast
data = ast.literal_eval(input("请输入列表: "))  # 只允许常量表达式

二、print() 只做有效输出

基础语法

print(*objects, sep=' ', end='\n', file=sys.stdout, flush=False)

业务实战技巧

1. 实时输出与进度条(flush 参数)

长时间任务中,用户需要看到实时反馈:

import time

for i in range(100):
    print(f"\r进度: {i+1}/100 [{('=' * (i+1))}{' ' * (99-i)}]", end="", flush=True)
    time.sleep(0.1)
print()  # 最后换行

# 关键点:flush=True 强制立即输出,否则会被缓冲区延迟

2. 重定向到日志文件

调试时同时输出到屏幕和文件:

import sys
from datetime import datetime

log_file = open("app.log", "a", encoding="utf-8")

def log(*args, **kwargs):
    """同时打印到控制台和日志文件"""
    timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    message = " ".join(map(str, args))
    print(f"[{timestamp}] {message}", file=sys.stdout)  # 屏幕
    print(f"[{timestamp}] {message}", file=log_file)    # 文件
    log_file.flush()  # 确保持久化

log("系统启动", "版本 1.0")

3. 结构化表格输出(sep 与格式化)

业务报表的简易展示:

headers = ["ID", "Name", "Score"]
data = [
    [1, "Alice", 95],
    [2, "Bob", 87],
    [3, "Charlie", 92]
]

# 打印表头
print(f"{headers[0]:<5} {headers[1]:<10} {headers[2]:<5}")
print("-" * 25)
for row in data:
    print(f"{row[0]:<5} {row[1]:<10} {row[2]:<5}")

4. 日志级别替代方案(小型脚本)

不适合引入 logging 模块的轻量级场景:

import sys

DEBUG = True

def debug_print(*args, **kwargs):
    if DEBUG:
        print("[DEBUG]", *args, **kwargs)

def error_print(*args, **kwargs):
    print("[ERROR]", *args, file=sys.stderr, **kwargs)  # 错误输出到 stderr

debug_print("连接数据库...")  # 只有 DEBUG=True 才显示
error_print("连接失败!")      # 始终显示,且区分输出流

用 logging 替代 print

在真实业务代码(Web 服务、数据处理管道)中,print 是反模式:

# 错误:无法关闭、无法控制级别、无法轮转
print(f"用户 {user_id} 登录成功")

# 正确:可配置、可分级、可输出到 ELK/Splunk
import logging
logger = logging.getLogger(__name__)
logger.info("用户登录成功", extra={"user_id": user_id})

print 仅适用于:

  • 命令行工具 (CLI)
  • 一次性脚本
  • 教学/调试代码
  • Jupyter Notebook 探索性分析

核心设计原则

  • 所有 input() 都经过验证
  • 使用 file=sys.stderr 区分正常输出和错误
  • 提供确认机制、默认值、清晰的提示
  • 将 I/O 逻辑与业务逻辑分离,便于单元测试

总结

函数关键注意点
input()永远返回字符串;需要验证类型;敏感信息用 getpass;防止 eval 注入
print()用 flush=True 实时输出;生产环境用 logging;错误输出到 stderr

input() 的核心是验证,print() 在生产环境中应替换为 logging。

📌 本文是 python-basics 的专题展开。