Skip to content
This page has been auto-translated and may contain errors.View in English

输出和输入

docs.scrimba.com

您从编写第一行代码开始就会使用的两个工具:print() 在终端中显示值,input() 从用户获取文本。它们很简单,但了解它们的行为可以让您避免早期的一些惊喜。

print()input() 是 Python 的标准终端 I/O 函数。两者都比初看起来更可配置。print() 接受控制值如何连接以及输出如何结尾的参数。input() 总是返回字符串,这决定了您如何处理来自用户的每个值。

在幕后,print() 写入 sys.stdoutinput() 读取 sys.stdin(您的终端连接的标准输出和输入流)。两者都只处理文本,输出默认是缓冲的:Python 会短暂保留它并分块写入,而不是一次写入一个字符。在真实程序中有两个重要的后果:输出进入与错误不同的流(stdout 与 stderr),并且您可以在需要立即显示输出时强制刷新该缓冲区。

Python 如何运行您的代码

Python 从上到下逐行运行您的代码,完全按照您编写的顺序。没有跳转。您编写的顺序就是它们运行的顺序。总是这样。

python
city = "北京"
print(city)
print("人口:2100万")

首先 city 被赋值。第一个 print 运行。第二个 print 运行。每次都是这个顺序。

这很重要,因为您不能在赋值之前使用变量。Python 还没有看到它,会引发错误:

python
print(country)   # NameError: 还没有定义 country
country = "中国"

当您的程序增长时要牢记这一点:任何您使用的东西都必须在使用它之前定义。

Python 使用**顺序执行**:每条语句在遇到时被计算,按照文件的单一从上到下的遍历。没有事先扫描整个文件。在赋值之前引用一个名字会在那一行抛出 NameError,而不是更早。

python
city = "北京"
print(city)             # 有效:city 已经绑定
print(country)          # NameError: 还没有赋值
country = "中国"

规则:依赖项必须在使用它们的行之前定义。

Python 按照文件的单一从上到下的遍历运行您的文件,它只在那一行实际运行时查找名字,而不是在它第一次读取文件时。这就是为什么一个缺失的名字是运行时的 NameError(当该行执行时)而不是您保存文件时的错误。没有提升(JavaScript 悄悄将 var 声明移到其作用域顶部的方式)也没有前向声明(C 让您现在承诺一个名字并稍后定义它的方式)。一个名字在它被赋值的时刻就存在,任何在此之前的使用都会抛出。

python
print(country)   # 运行时的 NameError,不是语法错误
country = "中国"
JunoPython 如何运行您的代码 Python 从上到下读取您的文件,一次一行,按照您编写的顺序。要点是让每个人都迷惑一次:一个名字必须在使用它的行之前存在,否则您会得到 NameError。没有跳过,也不是先读整个文件。
JunoPython 如何运行您的代码 从上到下,一遍遍历,按顺序。一个名字必须在任何使用它的行之前被赋值,否则在那一行就是 NameError。没有文件预扫描。
JunoPython 如何运行您的代码 一个从上到下的遍历,名字在每一行运行时被查找。在赋值之前使用一个名字会得到运行时的 NameError,不是语法错误。Python 不会提升,没有前向声明,所以顺序真的很重要。

打印输出

print() 是 Python 与您沟通的方式。传给它任何值,它就显示那个值。它会自动将您给它的任何东西转换为文本。

print() 通过 str() 将每个参数转换为字符串,用分隔符(默认是空格)连接它们,然后将结果后面跟着换行符写入标准输出。理解默认值使可选参数更可预测。

print() 的完整签名是 print(*objects, sep=' ', end='\n', file=sys.stdout, flush=False)*objects 收集器接受任何数量的位置参数并对每个调用 str()sepend 控制格式;file 将输出重定向到任何可写流;flush 强制立即清空缓冲区。

python
print("Hello")    # Hello
print(42)         # 42
print(3.14)       # 3.14
print(True)       # True

多个值

您可以通过用逗号分隔来一次打印多个值。Python 默认在它们之间放一个空格。使用 sep 改变分隔符:

多个位置参数各自用 str() 转换,然后由 sep 连接。默认 sep 是单个空格。覆盖它让您生成格式化的输出而不需要字符串连接:

print() 不会构建一个字符串然后写它;它一次一个地将每个参数写到流(对每个调用 str()),之间有 sep 最后有 end。因为这些写入一次发生一个而不是作为单一锁定的操作,两个同时运行的 print() 调用(在不同线程上)可以交错它们的输出。当这很重要时,构建一个字符串并打印一次。对于日常使用,覆盖 sep 仍然比手动粘合字符串好:

python
name = "张三"
age = 28
print(name, age)                        # 张三 28
print("Name:", name)                    # Name: 张三
print("2024", "01", "15", sep="-")     # 2024-01-15
print("a", "b", "c", sep=", ")         # a, b, c

控制行尾

每个 print() 调用默认以换行符结尾,所以下一个输出开始在新的一行。使用 end 改变那个。设置 end="" 使下一个 print 继续在同一行:

end 参数替换默认的换行符。将其设置为 "" 来抑制换行符,设置为 " " 来停留在同一行并带一个空格,或任何其他字符串。结合 sep,您可以生成大多数输出格式而不需要手动构建字符串:

sepend 是仅关键字的(您按名字传递它们,像 end="",从不按位置),默认为空格和换行符。end="" 是打印部分行并让下一个调用继续它的标准方式。对于需要显示为发生的输出,像进度指示器或实时日志,添加 flush=True 使 Python 立即写而不是等待换行符或缓冲区填满。

python
print("Loading", end="")
print("...")
# Loading...

print("one", end=" | ")
print("two", end=" | ")
print("three")
# one | two | three
Juno打印输出print() 将您给它的任何东西转换为文本并显示它。传递几个用逗号分隔的东西,您得到它们之间的空格,用 sep 改变那个。每个 print 在末尾掉到新的一行;end="" 保持下一个在同一行。
Juno打印输出print() 对每个参数运行 str(),用 sep 连接它们(默认是空格),并以 end 结束(默认是换行符)。覆盖任一个来格式化输出而不粘合字符串。
Juno打印输出print() 通过 str() 写每个参数,之间有 sep 最后有 end,两者仅关键字。使用 end="" 来继续一行,使用 flush=True 当输出需要立即显示时。

使用 f-string 格式化输出

构建消息的最简洁方式是 f-string。在开引号前放 f,然后在任何变量或表达式周围用花括号包装。Python 在运行时填充它。您可以在 {} 内放任何值、计算或方法调用。

f-string 在运行时计算 {} 内的任何表达式并将结果作为字符串嵌入。冒号后面的值引入一个格式规范:一个用于控制小数位数、对齐和数字格式的紧凑语法。它们比连接更快且更可读,并且不需要显式的 str() 调用。

f-string 中的每个 {} 最终调用 format(value, spec),它交给值自己的 __format__ 方法(一个dunder,Python 调用以格式化值的双下划线方法)。所以任何类都可以通过定义 __format__ 来决定它在 f-string 内的外观。转换标志 !r!s!a 首先运行 repr()str()ascii()!r 是要记住的用于调试,因为它显示一个值的方式就是您会在代码中键入的(引号、转义和所有),在下面的实践中显示。

python
name = "张三"
score = 980

# 连接:笨拙,需要 str() 进行数字
print("Player: " + name + ", Score: " + str(score))

# f-string: 可读,无需手动转换
print(f"Player: {name}, Score: {score}")

您可以在 {} 内放任何表达式:算术、方法调用(像 .upper(),在字符串章节中完整覆盖)、格式规范:

python
price = 49.99
tax = 0.2
total = price * (1 + tax)

print(f"Total: {total:.2f}")          # Total: 59.99
print(f"Name: {name.upper()}")        # Name: 张三
print(f"2 + 2 = {2 + 2}")             # 2 + 2 = 4

: 后的格式规范控制值如何显示:

python
ratio = 0.8765
count = 1234567
label = "revenue"

print(f"{ratio:.1%}")       # 87.6%
print(f"{count:,}")         # 1,234,567
print(f"{label:>12}")       # "     revenue"

:.2f 意思是"两个小数位"。您会不断地为价格和测量使用它。其他一切在您需要时就在那里。主要的事情:任何东西都可以在 {} 内,不仅仅是变量名。

:.2f:.0% 覆盖大多数格式需求。对齐符(><^)带宽度生成整洁的表格输出。一般模式是 {value:[align][width][.precision][type]}。一旦您识别了各部分,任何规范都是可读的而不需要记住所有组合。

同一个规范对不同类型可能意味着不同的东西,因为每个类型都格式化自己。f"{'hi':5}" 在右侧填充文本(文本默认左对齐)而 f"{42:5}" 在左侧填充(数字默认右对齐):同样的 :5,相反的结果。每个内置类型都为这个格式迷你语言携带自己的规则,自定义类通过定义 __format__ 设置自己的。

Juno使用 f-string 格式化输出 在开引号前放 f 并在 {} 中包装任何东西:一个变量、一个和,甚至一个方法调用。冒号添加格式,像 :.2f 用于两个小数位。比用 + 粘合字符串整洁得多。
Juno使用 f-string 格式化输出f"..." 直接将任何表达式的结果放入字符串,无需 str()。冒号后面是格式规范::.2f:,:>12 等等。比连接更清洁且更快。
Juno使用 f-string 格式化输出 每个 {} 调用值自己的 __format__,所以冒号后的规范意味着无论那个类型决定什么。!r 是要记住的:它显示一个值的方式就是您会键入的,这使得尾随空格和其他不可见字符在您调试时显眼。

从用户获取输入

input() 暂停您的程序并等待用户键入东西。他们键入的任何东西(并按 Enter)都作为返回值回来。括号中的字符串是用户看到的提示。

python
name = input("你叫什么名字? ")
print(f"你好,{name}!")

input() 总是返回一个字符串,无论用户键入什么。键入 42 您得到 "42",不是数字 42。要用它进行算术,显式转换:

python
age = int(input("你多大了? "))
print(f"十年后你会是 {age + 10}。")

如果用户键入不能转换的东西怎么办?Python 抛出 ValueError。正确处理这个问题在文件和异常章节中覆盖。

input() 将提示写入标准输出,从标准输入读取一行,去掉尾随换行符,并将结果作为字符串返回。没有类型推断。来自终端的一切都作为文本到达;您通过在边界显式转换来声明您需要什么类型。

python
name = input("你叫什么名字? ")
age = int(input("你多大了? "))

这个模式(接收文本,转换为您需要的类型)适用于任何地方外部数据到达。int()float()str() 是转换工具。如果字符串不能转换,Python 抛出 ValueError,在文件和异常章节中覆盖。

input() 是一个薄包装:它将提示写入 sys.stdout(标准输出),刷新它使提示在程序阻塞之前显示,从 sys.stdin(标准输入)读一行,去掉尾随换行符,并交回 str。它总是 str,由设计,因为 Python 不能猜测 "42" 是否意味着整数、浮点数或字面文本。所以您在边界处转换。这是 Python I/O 的基本形状:外部数据作为文本到达,您的代码在入口点决定其类型。

python
name = input("输入您的名字:")
score = float(input("输入您的分数(0.0 到 1.0):"))

print(f"Name:  {name!r}")      # !r 显示任何不可见的空白
print(f"Score: {score:.1%}")
Juno从用户获取输入input() 显示您的提示,等待一行,并总是作为 string 交回,即使他们键入一个数字。需要一个数字?在它进来时用 int()float() 自己转换它,然后用正确的类型继续。
Juno从用户获取输入input() 总是返回 str,没有类型推断。在边界用 int()float() 转换,并期望一个 ValueError 如果文本不解析。那个接收-然后-转换的模式在任何地方外部数据出现时出现。
Juno从用户获取输入 来自 `input()` 的一切都是 `str`,由设计,因为 Python 不能猜测您的意思。在入口点转换并在它不会解析时处理 `ValueError`。与任何其他外部数据着陆的地方的形状相同。

写入 stderr

默认情况下,print() 写入标准输出:在终端中出现并流入管道的流。Python 也有标准错误,一个用于诊断和警告的单独流。它们在终端中看起来相同但不同:当您将脚本的输出管道到另一个命令时,只有 stdout 通过。Stderr 总是到达终端。

写入 stderr 使用 print()file 参数。这需要导入 sys,在模块章节中覆盖。现在,知道两个流存在以及为什么它们是单独的就够了。

实践中

一个从用户输入个性化自己的测验:

python
name = input("你叫什么名字? ")
subject = input("哪个科目? ")

print(f"好吧,{name}。开始您的 {subject} 测验。")
print("祝您好运!")

两个输入都作为字符串回来并直接进入 f-string。不需要转换因为您将它们用作文本,不是数字。

一个有对齐表格输出的温度转换器:

python
celsius = float(input("摄氏度温度:"))
fahrenheit = celsius * 9 / 5 + 32

print(f"{'摄氏度':>12} {'华氏度':>12}")
print(f"{celsius:>12.1f} {fahrenheit:>12.1f}")

float() 处理来自用户的整数和小数。:>12 规范无论数字有多少位都保持列对齐。尝试输入 100-40:输出要么保持整洁。

使用 !r 来呈现 Python 确切接收到的,当输出看起来错误时很有用:

python
name = input("输入您的名字:")
score = float(input("输入您的分数(0.0 到 1.0):"))

print(f"Name:   {name!r}")
print(f"Score:  {score!r}")
print()
print(f"Result: {name}: {score:.1%}")

{name}{name!r} 当输入是干净的时显示相同。差异在有尾随空格或其他不可见字符时出现。养成在调试期间使用 !r 的习惯使得意外值立即可见。