1

unicode 和字符串编码仍然让我有些头疼。我按照这个问题/答案能够在消息中添加特殊字符(äÄÜ..)。

对于以下结构,我很难理解为什么第 2 版有效而第 1 版无效。

我的模型:

# -*- coding: utf-8 -*-
from __future__ import unicode_literals

class Project(models.Model):
    """
    Representation of a project
    """

    name = models.CharField(max_length=200)

    def __unicode__(self):
            return '%s ' % (self.name)

版本 1:

# -*- coding: utf-8 -*-
from __future__ import unicode_literals

def print_project(self, project):
        project_prefix = "Project: "
        print (project_prefix + str(project))

版本 2:

 # -*- coding: utf-8 -*-

def print_project(self, project):
        project_prefix = "Project: "
        print (project_prefix + str(project))

如您所见,唯一的区别是我执行此from __future__ import unicode_literals导入。抛出的错误如下:

'ascii' codec can't decode byte 0xc3 in position 0: ordinal not in range(128)
4

1 回答 1

3

在该__future__语句之后,您的文字不是str对象,而是unicode对象。这就是声明的全部要点。__future__这在他们所引用的文档或PEP 3112中都没有很好地描述(bytes考虑到字符串文字现在是 Unicode,大部分时间都在谈论如何编写 Python 2 样式的对象)。但这就是它的作用。

您可以在交互式解释器中对此进行测试:

>>> 'abc'
'abc'
>>> from __future__ import unicode_literals
>>> 'abc'
u'abc'

因此,在版本 2 中,您将两个str对象添加在一起,这很容易。但在版本 1 中,您添加了 aunicode和 a str。这是通过使用默认编码(即 ASCII)自动将 转换str为 aunicode来实现的,这不起作用。


解决此问题的最简单方法是使其project成为一个unicode自身:

def print_project(self, project):
    project_prefix = "Project: "
    print (project_prefix + unicode(project))

事实上,不管有没有这个__future__语句,这都会起作用——有了它,project_prefix已经是unicode; 没有它,它是一个str并且将从 ASCII 解码,但这很好,因为它ASCII。

如果你想使用非 ASCII 文字(在 project_prefix 中),并且你希望你的代码在有和没有__future__语句的情况下工作,你将不得不手动解码:

def print_project(self, project):
    project_prefix = "Project: ".decode('utf-8')
    print (project_prefix + unicode(project))

(当然,请确保匹配源文件的编码声明。)


在评论中,你问:

使用__future__import 语句时,我还需要在 .py 文件的开头定义编码吗?# -- 编码:utf-8 --

简短的回答是肯定的。

我不知道文档是否直接涵盖了任何地方,但是如果您考虑一下,没有其他方法可以工作。

为了将 8 位源代码中的文字解释为 Unicode,Python 编译器必须对它们进行解码。它知道从什么解码它们的唯一方法是您的编码声明。

另一种看待这一点的方式是,__future__就字符串文字而言,该语句使 Python 2 像 Python 3 一样工作,而 Python 3 需要编码声明。

如果您想自己测试,请将以下内容复制为 UTF 并将其粘贴到文本文件中。(请注意,您必须使用不理解编码声明的编辑器来执行此操作 - 诸如 emacs 之类的东西可能会在保存时将您的 UTF-8 文本转换为 Latin-1!)。

# -*- coding: latin-1 -*-
from __future__ import unicode_literals
print repr('é')

当你运行它时,它会打印出来u'\xc3\xa9',而不是u'\xe9'.

如果您不指定编码,Python 3 默认为 UTF-8,而 Python 2.5-2.7 默认为 ASCII,即使使用unicode_literals. 因此,您仍然需要编码声明。(添加总是安全的,即使在 3.x 中,它也让许多程序员的文本编辑器很高兴,所以这可能是一个值得保持的习惯,直到我们走得足够远以至于没有人记得 Latin-1 和 Shift-JIS 和cp1250 等等。)

于 2013-06-11T19:29:57.173 回答