解释Python3与Python中sys.stdin的行为
我需要写一段代码,让它在两个版本的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 个回答
在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()
。
这里的区别在于,CPython 2 使用 C 语言的标准输入输出库来实现像 sys.stdin
和 sys.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.stdout
和 sys.stdin
指向同一个地方(例如,如果它们都是 tty),那么在从 stdin 读取之前,stdout 应该默认被刷新。你可以在 Python 的错误追踪器上报告这个问题。
你把 flush()
和 print()
的顺序搞反了。正确的顺序是:
# First, write to stdout
print("input: ", end="")
# Then, flush all data in the stdout buffer
sys.stdout.flush()