为什么 Python 2.7 的输出与 Python 3.10 的输出不同?

Why is the output different with Python 2.7 than Python 3.10?

$ /usr/bin/python2 simple.py 200 > out2.pbm
$ /opt/src/Python-3.10.1/bin/python3 simple.py 200 > out3.pbm
$ cmp out2.pbm out3.pbm
out2.pbm out3.pbm differ: byte 304, line 3

python2 输出是正确的。 python3 输出不正确。

这里是a correct .pbm output file

simple.py 是

import sys

w = h = x = y = bit_num = 0
byte_acc = 0
i = 0; iterations = 50
limit = 2.0
Zr = Zi = Cr = Ci = Tr = Ti = 0.0

w = int(sys.argv[1])
h = w

sys.stdout.write("P4\n%d %d\n" % (w, h))

for y in range(h):

    for x in range(w):

        Zr = Zi = 0.0 
        Cr = (2.0 * x / w - 1.5); Ci = (2.0 * y / h - 1.0)        
        
        for i in range(iterations):

            Tr = Zr*Zr - Zi*Zi + Cr
            Ti = 2*Zr*Zi + Ci          
            Zr = Tr; Zi = Ti               
            if Zr*Zr+Zi*Zi > limit*limit:
                break
                        
        if Zr*Zr+Zi*Zi > limit*limit: 
            byte_acc = (byte_acc << 1) | 0x00
        else:
            byte_acc = (byte_acc << 1) | 0x01
                
        bit_num += 1         

        if bit_num == 8:
        
            sys.stdout.write(chr(byte_acc))            
            byte_acc = 0
            bit_num = 0

        elif x == w - 1:

            byte_acc = byte_acc << (8-w%8)
            sys.stdout.write(chr(byte_acc))  
            byte_acc = 0
            bit_num = 0

什么变化可能导致不同的输出?

我无法在 Python 3.10.1(Windows,64 位)下 运行:

**Traceback (most recent call last):
  File ... simple.py", line 39, in <module>
    sys.stdout.write(chr(byte_acc))
  File  ... \lib\encodings\cp1252.py", line 19, in encode
    return codecs.charmap_encode(input,self.errors,encoding_table)[0]
UnicodeEncodeError: 'charmap' codec can't encode character '\x80' in position 0: character maps to <undefined>

如果我将 chr() 的 2 个实例更改为 str()(以便它打印出字节的十进制值的字符串表示形式),它会在 3.10.1 和下产生相同的输出2.7.11.

因此,无论您的 Python 在 Python 下为 sys.stdout 使用任何默认的 Unicode 编码方案,您都会被烧毁 3.

如果我像这样设置一个 envar(语法可能与您的 OS 不同):

set PYTHONIOENCODING=latin-1

然后两个 Python 使用 chr() 产生相同的输出。

一种方式

这是“修复它”的一种方法:

import sys
from sys import stdout

if hasattr(stdout, "buffer"): # Python >= 3
    def putbyte(b):
        assert 0 <= b < 256
        stdout.buffer.write(bytes([b]))
else: # before Python 3
    def putbyte(b):
        assert 0 <= b < 256
        stdout.write(chr(b))

然后更改您的代码以使用 putbyte(byte_acc) 而不是当前的 sys.stdout.write(chr(byte_acc))

但这还不够。自己写入内部缓冲区也使您负责跨用途的缓冲区管理。当前

之后
sys.stdout.write("P4\n%d %d\n" % (w, h))

您还需要添加

sys.stdout.flush()

在添加额外的输出字节之前将输出字符串放入缓冲区