Python 在 __str__ 中出现“序号超出范围”错误,但在 print 中没有

0 投票
1 回答
517 浏览
提问于 2025-04-18 11:46

我有一个对象,它可以从一个JSON文件中读取一些数据。这个JSON数据里有些字符串包含拉丁1字符,比如:

"name" : "frisée"

我把这个对象转换成一个表格,里面包含从JSON对象中读取的一些字符串。如果我这样写:

def __str__(self): # Never mind the details, what matters is that I use __str__
    ts = [p + (count,) for p, count in self.counts.items()]
    ts.sort(key=lambda x:(x[2], x[0], x[1]))
    return "\n".join(["%s\t%s\t%s" % (t[0], t[1], t[2]) for t in ts])

然后尝试运行 print MyObject(),我就会遇到错误:

UnicodeEncodeError: 'ascii' codec can't encode character u'\xe9' in position 9112: ordinal not in range(128)

但是如果我这样写:

def to_string(self):
    ts = [p + (count,) for p, count in self.counts.items()]
    ts.sort(key=lambda x:(x[2], x[0], x[1]))
    return "\n".join(["%s\t%s\t%s" % (t[0], t[1], t[2]) for t in ts])

然后运行 print MyObject().to_string(),一切就正常了。字符"é"也能正确显示。

为什么 __str__ 的表现和 to_string 不一样呢?我该怎么做才能让 __str__ 版本也能正确打印出来?

我尝试了各种不同的 encode 方法,但都没有成功。

1 个回答

1

你的 __str__ 方法返回的是一种 unicode 类型的值,而 JSON 字符串总是 Unicode 格式的。不过,__str__ 的返回值必须 始终字节串,也就是 str 类型。如果你不这样做,Python 会自动帮你调用 str(),这就意味着它会用默认的 ASCII 编码来处理任何 Unicode 字符。

你可以明确地对结果进行编码:

def __str__(self):
    ts = [p + (count,) for p, count in self.counts.items()]
    ts.sort(key=lambda x:(x[2], x[0], x[1]))
    return u'\n'.join([u'\t'.join(t[:2]) for t in ts]).encode('utf8')

或者可以使用 __unicode__ 方法。不过,这个方法不会被 print 自动调用;你需要明确地使用 print unicode(MyObject()) 来调用它。

如果你的终端设置正确,print 知道如何正确编码 unicode 字符串。它会使用 sys.stdout.encoding 来明确地编码你的 Unicode 数据。这就是为什么 MyObject().to_string() 能正常工作的原因。

撰写回答