我在遇到旧代码时也遇到了同样的问题。这似乎是 Python 2 的 file
对象的 __next__
方法实现的问题;它使用了一个 Python 级别的缓冲区(-u
/PYTHONUNBUFFERED=1
并不影响它,因为它们只是取消了 stdio
的 FILE*
自身的缓冲,但是 file.__next__
的缓冲与此无关;同样,stdbuf
/unbuffer
不能改变任何缓冲,因为 Python 替换了 C 运行时制作的默认缓冲区;对于新打开的文件,file.__init__
最后一件事就是调用 PyFile_SetBufSize
,它使用 setvbuf
/setbuf
[API] 来替换默认的 stdio
缓冲区)。
当你有一个如下形式的循环时,就会出现问题:
for line in sys.stdin:
在编程中,第一次调用__next__
(由for
循环隐式调用以获取每个line
)会阻塞以填充块,然后才能生成单个行。
有三种可能的解决方法:
(仅适用于Python 2.6+)使用io模块(从Python 3中作为内置模块回溯)重新包装sys.stdin,以完全绕过file,而采用(实际上更优秀的)Python 3设计(每次使用单个系统调用填充缓冲区,而不会阻塞整个请求读取的时间;如果请求4096字节并获得3个字节,则它将查看是否有可用行并在有时生成它),因此:
import io
import sys
# 如果您不总是完全消耗stdin,则添加buffering = 0参数,以便您无法在包装器的缓冲区中丢失数据。但是,使用buffering = 0会更慢。
with io.open(sys.stdin.fileno(), 'rb', closefd=False) as stdin:
for line in stdin:
# 处理该行
这通常比选项2更快,但更冗长,并需要Python 2.6+。通过将模式更改为'r'并可选地传递输入的已知编码(如果它不是区域设置默认值),可以使重新包装对Unicode友好,以无缝获取unicode行而不是(仅限ASCII)str。
(任何版本的Python)通过使用file.readline解决file.__next__的问题;尽管意图相似,但readline不会执行自己的(过度)缓冲,它将委托给C stdio的fgets(默认构建设置)或手动循环调用getc / getc_unlocked到一个缓冲区,该缓冲区在精确命中行尾时停止。通过将其与两个参数的iter结合使用,您可以获得几乎相同的代码而不会有过多的冗余(它可能比先前的解决方案慢,这取决于fgets是否在后台使用以及C运行时如何实现它):
# ''是结束循环的标记;readline在EOF处返回''
for line in iter(sys.stdin.readline, ''):
# 处理该行
迁移到Python 3,它没有这个问题。 :-)
myparser
中作为子进程启动batch_job
,这样你就完全控制 STDOUT / STDIN 了呢?你现有的设置不仅依赖于 Python,还依赖于 shell 缓冲本身。 - zwer