python3与python2的区别

Python2 的默认编码是 asscii,这也是导致 Python2 中经常遇到编码问题的原因之一

Python 3 默认采用了 UTF-8 作为默认编码,因此不再需要在文件顶部写 # coding=utf-8 了

# python2.7

>>> sys.getdefaultencoding()

'ascii'

# python3.5

>>> sys.getdefaultencoding()

'utf-8'

字符串

版本

字节码

字符串

默认编码

python2

str

unicode

ascii

python3

bytes

str

utf-8

py2 想定义一个日常的字符串,需要手动 u"xxx","xxx" 定义的是当前编码设定下的字节码。

# python2

>>> a="你好"

>>> type(a)

>>> isinstance(a, unicode)

False

>>> isinstance(a, bytes)

True

>>> a=u"你好"

>>> type(a)

在 py3 中,"xxx" 定义的就是字符串(unicode编码),b"xxx" 定义的才是字节码。

>>> a = "你好"

>>> type(a)

>>> isinstance(a, bytes)

False

>>> a = u"你好"

>>> type(a)

>>> a = b"你好"

File "", line 1

SyntaxError: bytes can only contain ASCII literal characters.

>>> a = b"secret"

>>> type(a)

所以在 py3 中,coding: utf-8 的注释没有任何用处了。在 py2 中它会隐式的根据指定编码对 str 做字节码的转换,但在 py3 中已经没有这种隐式的转换和定义了。

py2编码问题的坑

在 py2 中经常遇到的错误:

if __name__ == "__main__":

a="你好"

# File "test_py2.py", line 2

# SyntaxError: Non-ASCII character '\xe4' in file test_py2.py on line 2, but no encoding declared; see http://python.org/dev/peps/pep-0263/ for details

在py2中会隐式地尝试使用默认编码方式(ascii)将 "你好" 编码为字节码,而ascii编码无法对中文编码,因此报错

# coding=utf-8

if __name__ == "__main__":

"你好".encode('utf-8')

# Traceback (most recent call last):

# File "test_py2.py", line 4, in

# "你好".encode('utf-8')

# UnicodeDecodeError: 'ascii' codec can't decode byte 0xe4 in position 0: ordinal not in range(128)

首先我们已经知道py2中默认字符串为字节码,那么"你好"应该是一个utf-8编码方式(文件定义的编码方式)生成的字节码,怎么能对字节码再进行编码呢?

因为 py 以 unicode 作为运算基准,所以在执行编码操作前会先隐性地将字节码 decode 至 unicode,并使用默认字符集(并非文件定义的编码方式)ascii做解码,因此就无法解码utf-8编码形成的字节码

而在 py3 中是不允许这种操作的。从 bytes 到另一 bytes 你必须先 decode 后才能 encode。

Unicode与字节码

Unicode是什么

Unicode的发明目的是让世界上的每一个符号都有一个独一无二的编码

Unicode 是一个符号集,它规定了符号的二进制代码,却没有规定这个二进制代码应该如何存储。

为什么不直接存储Unicode编码?

符号的Unicode编码有的长有的短。

比如 'a' 的unicode编码是7位,一个字节就能存下(与ascii编码相同)

>>> ord("a")

97 // Unicode编码的十进制表示法

>>> hex(ord("a"))

'0x61' // 转成16进制就是我们通看到的Unicode编码格式

>>> bin(ord("a"))

'0b1100001' // 转成二进制,可以看到 "a" 的Unicode编码有7位,一个字节

而汉字的unicode编码通常需要两个字节才能存下

>>> ord("严")

20005

>>> hex(ord("严"))

'0x4e25'

>>> bin(ord("严"))

'0b100111000100101' // “严”的unicode编码有15位,需要两个字节才能存下

计算机并不知道几个字节表示一个符号,这就给存储和读取带来了不便。如果统一用两个字节存储一个符号,那么对于英文字符来说,前一个字节就全部是0了,这样是对存储空间的浪费。

因此,需要一种聪明的方法来存储Unicode编码,既不要浪费存储空间,也要让机器能够分辨几个字节表示一个符号

UTF-8编码

UTF-8编码是Unicode的一种实现方式,它将Unicod编码分为四个范围,用1-4个字节表示一个字符,即:

1)对于单字节的符号,字节的第一位设为0,后面7位为这个符号的 Unicode 码。因此对于英语字母,UTF-8 编码和 ASCII 码是相同的。

2)对于n字节的符号(n > 1),第一个字节的前n位都设为1,第n + 1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的 Unicode 码。

Unicode符号范围 (十六进制)

UTF-8编码方式(二进制)

0000 0000-0000 007F

0xxxxxxx

0000 0080-0000 07FF

110xxxxx 10xxxxxx

0000 0800-0000 FFFF

1110xxxx 10xxxxxx 10xxxxxx

0001 0000-0010 FFFF

11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

跟据上表,计算机解读 UTF-8 编码非常简单。如果一个字节的第一位是0,则这个字节单独就是一个字符;如果第一位是1,则连续有多少个1,就表示当前字符占用多少个字节。

下面,还是以汉字严为例,演示如何实现 UTF-8 编码。

严的 Unicode 是4E25(100111000100101),根据上表,可以发现4E25处在第三行的范围内(0000 0800 - 0000 FFFF),因此严的 UTF-8 编码需要三个字节,即格式是1110xxxx 10xxxxxx 10xxxxxx。然后,从严的最后一个二进制位开始,依次从后向前填入格式中的x,多出的位补0。这样就得到了,严的 UTF-8 编码是11100100 10111000 10100101,转换成十六进制就是E4B8A5。

>>> "严".encode("utf-8")

b'\xe4\xb8\xa5'

>>> for b in "严".encode("utf-8"):

... print(type(b), b, bin(b))

...

# 字符串的元素是字符,bytes对象的元素则是字节

# 字节对象本质上是一个 0 <= x < 256 区间内的整数不可变序列

# 我们可以用方括号来取得每个字节

# 然后,用bin(b)来直观地看每个字节在内存中的存储方式

228 0b11100100

184 0b10111000

165 0b10100101