解释Python3与Python中sys.stdin的行为

2 投票
3 回答
516 浏览
提问于 2025-04-28 09:14

我需要写一段代码,让它在两个版本的Python中都能正常工作,但我不太明白这段代码的表现:

from __future__ import print_function
import sys
sys.stdout.flush()
print("input: ", end="")
f = sys.stdin.readline()
print(f)

当我用Python 2运行这段代码时,它的表现符合我的预期:

$ python2 test_input.py 
input: foo bar
foo bar

但是当我用Python 3运行这段代码时,它的表现就很奇怪了。它先读取输入,然后再打印提示信息:

$ python3 test_input.py 
foo bar
input: foo bar

你能解释一下这个情况,并给出一个解决办法吗?

暂无标签

3 个回答

0

在Python 3.3版本中,print()函数新增了一个叫flush的参数:

在3.3版本中更改: 添加了flush这个关键字参数。

在3.3之前的版本中,print()函数会总是刷新输出,即使你使用了end=''。你在Python 3.2中也会看到同样的情况:

$ python3.2 test.py 
foo bar
input: foo bar

要在Python 3.3中获得相同的效果,可以使用flush:

try:
    print("input: ", end="", flush=True)
except TypeError:
    print("input: ", end="")

或者在Python 3.3及以上版本中,在print()调用后使用sys.stdout.flush()

2

这里的区别在于,CPython 2 使用 C 语言的标准输入输出库来实现像 sys.stdinsys.stdout 这样的标准流(print() 就是用到 sys.stdout)。而 Python 3 则是在系统 API 的基础上重新实现了输入输出,比如在 POSIX 系统上使用的 open、read 和 write。

为了避免去思考 print() 是怎么实现的,如果直接使用 sys.stdout,也会遇到同样的问题:

# __main__.py
import sys

sys.stdout.write("input: ")
f = sys.stdin.readline()
sys.stdout.write("*" + f)

在 Python 3 中,"input: " 在调用 readline() 之前不会被打印出来:

$ python2 .
input: foo bar
*foo bar
$ python3 .
foo bar
input: *foo bar

在 C 语言中,在交互式情况下,stdout 会在读取任何输入之前被刷新(如果在同一个更新流中,输出操作后面紧跟着输入操作而没有使用 fflush(),那就是未定义的行为)。这个 C 程序会在请求输入之前如预期那样打印出 "input: "

#include <stdio.h>

#define MAXLEN 100

int main(void) {
  char buf[MAXLEN] = {0};

  fputs("input: ", stdout);
  if (!fgets(buf, MAXLEN, stdin))
    return 1;

  fputs(buf, stdout);
  return 0;
}

这就是为什么这个解决方法有效:在调用 sys.stdin.readline() 之前先调用 sys.stdout.flush(),这个方法是 @Dietrich Epp 提出的。

这是 Python 3 实现中的一个缺陷。如果 sys.stdoutsys.stdin 指向同一个地方(例如,如果它们都是 tty),那么在从 stdin 读取之前,stdout 应该默认被刷新。你可以在 Python 的错误追踪器上报告这个问题

2

你把 flush()print() 的顺序搞反了。正确的顺序是:

# First, write to stdout
print("input: ", end="")
# Then, flush all data in the stdout buffer
sys.stdout.flush()

撰写回答