本文是Python通用編程系列教程,已全部更新完成,實現(xiàn)的目標是從零基礎開始到精通Python編程語言。本教程不是對Python的內容進行泛泛而談,而是精細化,深入化的講解,共5個階段,25章內容。所以,需要有耐心的學習,才能真正有所收獲。雖不涉及任何框架的使用,但是會對操作系統(tǒng)和網絡通信進行全局的講解,甚至會對一些開源模塊和服務器進行重寫。學完之后,你所收獲的不僅僅是精通一門Python編程語言,而且具備快速學習其他編程語言的能力,無障礙閱讀所有Python源碼的能力和對計算機與網絡的全面認識。對于零基礎的小白來說,是入門計算機領域并精通一門編程語言的絕佳教材。對于有一定Python基礎的童鞋,相信這套教程會讓你的水平更上一層樓。
一 理解字符編碼理論儲備知識
1. 字符編碼說明
字符編碼這個知識點其實只是涉及到一行代碼,但是它非常重要,據(jù)不完全統(tǒng)計,現(xiàn)在軟件30%的損失都是由于亂碼問題所導致的,這個問題是最容易被大家所忽視的,因為使用的時候只是一行代碼的問題,但是它的里面包含很多的知識,大部分人更加傾向于直接掌握結果,而不考慮它內部的知識,這就導致了一旦遇到字符編碼的亂碼問題,就會手足無措。你之前可能看過一下相關的介紹,也可能聽過一些歪理邪說,這些介紹是否正確我們無從考證,在這篇文章中,我們會對字符編碼進行全方位的介紹。字符編碼的特點是理論非常多,而結論非常少,但是如果不知道理論,結論可能永遠也無法理解,而且以后遇到字符編碼問題就會不知所措。目前在網上幾乎沒有人能夠清晰的把它說明白,因為字符編碼的發(fā)展史涵蓋了整個計算機發(fā)展的過程,我們不會直接拿出現(xiàn)成的結論或者猜測的結論,而是會從客觀展示出來的打印結果來論證:我所講的就是正確的。
2. 計算機運行應用軟件原理
在正式介紹字符編碼之前,我們需要先了解計算機的運行與哪些核心部件相關:
CPU:運行程序
內存:臨時存儲數(shù)據(jù),一個軟件要想運行,必先加載到內存
硬盤:軟件的數(shù)據(jù)要想永久存儲,一定要存入硬盤
如下圖所示,一個應用軟件的啟動過程是:
- 應用軟件存放于硬盤上
- 應用軟件程序從硬盤讀入到內存
- CPU調用內存中與該應用軟件相關的數(shù)據(jù)進行處理

3. 保存文件的原理
接下里來我們再來看一下一個文本編輯器保存文件的過程:
- 如下圖所示,當我啟動notepad++程序之后,就是把這個程序讀入到內存
- 我在notepad++程序中輸入“Hello World”之后,這個內容臨時保存到了內存中
- 你現(xiàn)在看到的屏幕上的內容是,notepad++程序在你寫入的同時返回的結果
存在內存中的特點是:只要一斷電,數(shù)據(jù)就會丟失,所以,我們要想永久的保存數(shù)據(jù),需要在notepad++程序上執(zhí)行一個操作,把數(shù)據(jù)永久保存到硬盤(notepad++程序會給你附加一個自動保存到硬盤的功能,這是為了提升用戶的使用體驗,早期的程序沒有這個功能)。

4. 執(zhí)行Python程序的原理
接下來我們再來看一下Python解釋器執(zhí)行Python程序的原理,也是分為三個階段(以Python3解釋器和test.py文件為例):
- 先啟動Python解釋器(把Python3解釋器這個應用軟件讀入內存)
- 把test.py文件讀入內存
- Python3解釋器識別Python語法,解釋執(zhí)行test.py程序
注意:我們寫的Python代碼如果沒有解釋器解釋執(zhí)行和寫一個普通的文件沒有任何區(qū)別,這也就是說,你可以使用Pycharm寫Python代碼,也可以使用WPS寫Python代碼,在編寫Python程序時候沒有語法的概念(檢測語法Pycharm給你附加的功能,但其實本質也是一個文本編輯器),編寫的結果和編寫一個普通文件是沒有任何區(qū)別的, 只有在第三階段執(zhí)行Python程序的時候才會監(jiān)測語法。
二 字符編碼介紹
1. 字符編碼初次登場
計算機是基于電工作的, 高電平用數(shù)字1表示,低電平用數(shù)字0表示,計算機也只能識別010101這種東西,這就是二進制,計算機的工作原理就是基于二進制工作的。 我們平時在使用計算機的時候并不是使用二進制控制的,使用的都是人類的字符(中國人使用漢語,美國人使用英語),但是這些人類的字符計算機是看不懂的,要想讓計算機能夠看得懂,必先經歷一個過程:
人類的字符====>翻譯====>二進制數(shù)字
我們在notepad++程序中寫了一個“你好”,就把這個內容寫入到了內存中,計算機要想識別必先經歷一個翻譯的過程,這個翻譯肯定不能隨便翻譯的,因為在取這個數(shù)據(jù)的時候仍需按照二進制數(shù)字與人類的字符一一對應的取出來,所以必須要遵循一個標準,這個標準就是字符編碼表。<br />我們在notepad++程序中寫了一個“Hello World”,在計算機內部已經事先存好了這張表,每一個字母(包含大寫字母和小寫字母)和空格回撤標點符號這些東西都對應一個數(shù)字,然后再把這些數(shù)字轉化成二進制,這樣一個字符就會對應一組二進制數(shù)字,也就完成了寫入的過程,當打印的時候再反過來,一組二進制數(shù)字對應一個字符。所以,我們就清楚了內存上應該有這樣一張字符編碼表,早期的時候硬盤上保存的也是二進制,所以硬盤上無需有字符編碼表。
2. 字符編碼發(fā)展史
計算機起源于美國,美國人說英語,美國人當時設計的時候根本就沒考慮過中國人有一天也能用的起計算機,那么當時美國人設計的時候就只需要考慮計算能識別英文符號就可以了,所以當時的字符編碼表就只是英文字符與數(shù)字的對應關系,所有這些加起來一共120多種就夠了,那么也就應該有120多個不同的數(shù)字來表示這些字符。計算機的數(shù)字是二進制的,要想用010101這種東西表示出120多個數(shù)字應該用幾位二進制數(shù)?
一位二進制數(shù):只能表示0或者1兩個數(shù)字,除去0之外,只能表示1個數(shù)字
兩位二進制數(shù):00,01,10,11能表示3個數(shù)字
三位二進制數(shù):000,001,010,011,100,101,110,111能表示7個數(shù)字
四位二進制數(shù):能表示2**4-1 = 15個數(shù)字
最開始設計的時候設計了八位二進制數(shù),最小的是0000 0000,最大的是1111 1111,它能夠表示2 * * 8 - 1 = 255個數(shù)字(不包含0),但是最早期只是使用了后面的七位二進制位能夠表示1~127的數(shù)字就夠了,留下一位二進制位為了以后的擴展留一些余地。一個二進制位稱為一個比特位,8個比特位稱為一個字節(jié):8bit = 1bytes,美國人用8個比特位來表示一個英文字符,所以一個英文字符占一個字節(jié)。這個字符編碼表就是ASCII表,這也是最早的字符編碼表,如下圖所示。
后來中國人也開始使用計算機了,ASCII只有英文字符的對應關系,而且8個比特位最多能表示200多個字符的對印關系,如果有一個人說他認識200多個漢字,估計這個人小學還未畢業(yè)。在這樣的場景下,中國人也定制出了自己的字符編碼表,叫做GBK(gb2312),為了表示出更多的數(shù)字,GBK編碼表用16個比特位來表示一個中文字符,那么他所能表示的漢字個數(shù)就是2 * * 16 - 1 = 65535個,這個基本上也就涵蓋了所有我們常用的漢字。所以GBK使用兩個字節(jié)來表示一個中文字符,但是GBK來表示一個英文字符還是用一個字節(jié)(為了和ASCII統(tǒng)一)。
不只是中國人能用電腦,日本人和韓國人也能用電腦,日本人規(guī)定了自己的Shift_JIS編碼,韓國人規(guī)定了自己的Euc-kr編碼(另外,韓國人說,計算機是他們發(fā)明的,要求世界統(tǒng)一用韓國編碼,但世界人民沒有搭理他們)。
到了這個階段,如果每個國家的東西都是自己國家的人看,這當然沒有問題,但是顯然我們有這樣的需求,這就出現(xiàn)了一個問題,你的硬盤上可能有日本人和韓國人編碼的軟件,但是內存中的字符編碼表只有GBK,那么就會出現(xiàn)亂碼,你不是秦始皇,自然做不出他那么偉大是事情,所以必須找到一種能夠兼容萬國語言的字符編碼(其實6萬多就足夠表示了,各國語言雖然不同,但是所用的符號都非常類似),這個字符編碼就是Unicode,它使用16個比特位也就是兩個字節(jié)來表示一個字符。這也就意味著任何國家的數(shù)據(jù)到了內存中都是Unicode編碼,這樣內存中就不會出現(xiàn)亂碼的問題了。除此之外還有一個很重要的問題,之前用各國編碼寫的保存在硬盤上的文件不能廢棄,這是歷史遺留問題,所以Unicode還必須要有一個非常重要的功能:把各國編碼的文件轉化成Unicode,如下圖所以。這樣有了Unicode之后,一方面可以兼容萬國語言,另一方面老的軟件也可以在不同國家的機器上運行。
[圖片上傳失敗...(image-492369-1554347107151)]
至此,Unicode字符編碼已經解決的大部分的亂碼問題,但是還存在一個可以優(yōu)化的空間,內存中用Unicode編碼,硬盤上面有各國編碼的軟件,以后寫程序肯定是趨向于全部使用Unicode編碼,但是如果一篇文檔當部分都是英文,寫一個同樣的內容原來的ASCII一個英文字符占用一個字節(jié),而Unicode一個英文字符就會占用兩個字節(jié),這樣的話就會增加硬盤的占用并且加大了IO操作的時間(IO操作暫且先理解為讀寫操作,后面會有詳細說明)。本著節(jié)約的精神,又出現(xiàn)了把Unicode編碼轉化為“可變長編碼”的UTF-8(可變長,全稱Unicode Transformation Format)編碼。UTF-8編碼把一個Unicode字符根據(jù)不同的數(shù)字大小編碼成1-6個字節(jié),常用的英文字母被編碼成1個字節(jié),漢字通常是3個字節(jié),只有很生僻的字符才會被編碼成4-6個字節(jié)。如果你要傳輸?shù)奈谋景罅坑⑽淖址肬TF-8編碼就能節(jié)省空間。所以,現(xiàn)在硬盤上的字符編碼一般是“UTF-8”,內存中用的字符編碼一般是Unicode,以后肯定會趨向于內存和硬盤的存儲都是用UTF-8這種字符編碼,但是現(xiàn)階段都是被逼的,Unicode編碼還要負責轉換歷史遺留問題的編碼。
三 亂碼問題的產生與解決
1. 亂碼問題的成因
接下來就是我們做實驗的過程了,內存中的編碼都是Unicode,如果忽略硬盤,在內存中隨便些什么編碼都不會出現(xiàn)亂碼,但是因為硬盤的存在就會出現(xiàn)由內存向硬盤保存的時候你要指定一個字符編碼,比如說是GBK,這時就是由Unicode轉化稱GBK,當把這個硬盤文件重新在內存讀取的時候你也要告訴計算機按照GBK編碼來讀取,它才會對應的把數(shù)據(jù)由GBK編碼反解成Unicode編碼寫入到內存。如果你在這時告訴你計算機用ASCII的標準來反解數(shù)據(jù),那么就無法反解出原來保存的數(shù)據(jù)內容,計算機蒙圈了,呈現(xiàn)給你的就是它蒙圈后的結果。
2. 保證不亂碼的方案
保證不出現(xiàn)亂碼問題其實結論就只有一個:文件用什么編碼保存的,就用什么編碼讀取,注意:我們能控制的只是文件由內存保存到硬盤的編碼。
3. 人為制造亂碼
接下來我們會在notepad++程序上演示人為制造亂碼的過程,首先把程序保存文件的編碼改成日文的編碼,然在在程序里面先后寫入中文的“你瞅啥”和日文的“あなたを見て”(這是日文的“瞅你咋地”),接下里來保存。注意:在保存過程中,其實計算機已經不能識別中文的“你瞅啥”了,但是它不能報錯呀,他一定要硬存,所以這個保存的過程并沒有什么問題,你現(xiàn)在看到的結果還是在內存中的。

保存之后,我們關掉再重新讀取,先使用使用ASCII試一下會看到什么結果。

全都是亂碼,那么我們再改成日文的編碼試試。

日文“瞅你咋地”已經讀取出來了,但是我們存的漢字就讀不出來了,這時無論我們改成任何編碼漢字都是讀不出來的,因為保存的時候計算機就已經無法識別了,他并沒有完成有效的數(shù)據(jù)保存。所以,以后寫程序寫文件都應該用UTF-8編碼來寫,這樣就不會發(fā)生亂碼了。
四 Python中的字符編碼問題
1. 解釋器讀取文件時字符編碼異同
我們現(xiàn)在用GBK編碼在文件中寫一個“你好”,然后用Python解釋器來解釋執(zhí)行(不用Pycharm),你會發(fā)現(xiàn)在Python2 和Python3 中分別提示以下錯誤信息,如下圖所示,這就說明如果你不指定字符編碼:
Python3解釋器默認使用UTF-8編碼來讀
Python2解釋器默認使用ASCII來讀

為了解決這個問題,我們需要在寫Python文件開頭就指定好字符編碼,如下圖所示。

再次調用解釋器解釋執(zhí)行的時候就不會發(fā)生字符編碼的錯誤了。

文件頭的作用就是告訴Python解釋器用指定的字符編碼去讀取文件內容。
在Pycharm中,當我指定好了讀取文件的字符編碼,它會自動改變保存寫入到硬盤的字符編碼,如下圖所示。

很多人都知道在寫代碼的時候,文件最上方寫一行指定字符編碼的文件頭:
# coding:utf-8
但是可能并不知道原因,在Pycharm中,只要你這樣寫了就不會發(fā)生任何的字符編碼問題,但是如果換一個其他的IDE工具,這一行代碼代表的是讀文件所使用的字符編碼,如果這個IDE工具默認保存寫入到硬盤的字符編碼是“GBK”,而且他不會隨著你的文件頭而改變,那么它保存的時候就是以GBK保存的,而讀的時候指定了UTF-8的字符編碼,計算機就會讀不出來,換了人來解決這個問題,如果不清楚它的成因,也可能會很懵逼。好就好在大部分的文本編輯器默認用的字符編碼都是UTF-8,一般人們寫文件頭一般也是指定UTF-8為讀取的標準。所以,大部分不懂字符編碼的人出現(xiàn)錯誤可能性也比較低。
2.解釋器執(zhí)行文件時字符編碼異同
Python中有一種數(shù)據(jù)類型會涉及到字符的概念,這種數(shù)據(jù)類型就是字符串。我們在寫程序時候如果不指定字符編碼,保存的時候默認就是UTF-8的字符編碼,而Python3解釋器默認也是使用UTF-8來讀取的,所以,它在Python3中運行是沒有任何問題的,但是Python2中就不行了,所以為了執(zhí)行Python程序的前兩個階段不出現(xiàn)問題,我們統(tǒng)一的都會加上文件頭指定UTF-8字符編碼,但是到了第三個階段,就會開始識別語法了,我們在Pycharm中寫入如下代碼內容,計算機在執(zhí)行的時候會先把這些代碼以Unicode編碼讀入內存,然后Python解釋器檢測到代碼中要保存一個“上”這個字符串變量,解釋器就會調用計算機申請內存空間,把“上”這個字符存入到計算機內存中,保存的方式自然還是二進制,但是應該用什么字符編碼保存呢?
# coding:gbk
x = '上'
涉及到Python中的數(shù)據(jù)類型,這是龜叔寫的解釋器,自然是他說了算。
Python2中的字符串分為兩種類型:1 str,2 unicode
在Python2中如果指定字符編碼,那么字符串的保存會按照你指定的字符編碼來完成,正如上面的代碼,我們使用GBK編碼來指定,那么在內存中就會用兩個字節(jié)來保存一個漢字,接下來我們就來驗證一下,如果我們直接打印的這個x的話,從理論上講,x對應的就是一串二進制數(shù),而打印的時候Python2解釋器幫你做了一個轉化,目的是為了讓你能夠更加直觀的看到它的打印結果,但是會出現(xiàn)亂碼(這個亂碼問題我們最后再講),我們可以使用以下代碼這種形式來進行打印,就可以看到還沒有轉化之前的結果(龜叔還沒來得及在Python2中的做轉化,就寫了Python3)。
# coding:gbk
# 解釋器已經切換到Python2
x = "上"
# print(x) # Python2加括號也能打印
print([x, ])
"""
輸出:
['\xc9\xcf']
"""
我們從輸出結果上來看,“\x”代表的是十六進制,“c9”和“cf”分別代表兩個十六進制位,一個十六進制位對應四個二進制位,那么打印結果'\xc9\xcf'就是16個二進制位,也就是16bit=2bytes,這也就證明了GBK編碼保存中文用兩個字節(jié)。接下來用同樣的方式我們再來驗證一下UTF-8保存常見漢字用3個字節(jié),代碼如下。
# coding:utf-8
# 解釋器已經切換到Python2
x = "上"
# print(x) # Python2加括號也能打印
print([x, ])
"""
輸出:
['\xe4\xb8\x8a']
"""
3. 字符編碼的轉換
通過前面的講解我們已經清楚了unicode編碼可以轉換成GBK或者UTF-8,相反也可以轉換,他們之間的轉換過程如下:
unicode ===>編碼encode===>GBK/UTF-8
GBK/UTF-8 ===>解碼decode===>unicode
所以,必然會有以下代碼的執(zhí)行:
# coding:utf-8
# 解釋器已經切換到Python2
x = "上"
# print(x.decode('gbk')) # 使用utf-8編碼,使用gbk無法解碼
print(x.decode('utf-8')) # 使用utf-8編碼,使用utf-8解碼
print([x.decode('utf-8'), ]) # 在列表打印出龜叔沒有轉換之前的unicode編碼
"""
輸出:
上
[u'\u4e0a'] # \u 代表unicode
"""
我們來比較一下unicode字符編碼表中“上”這個字符,下圖是unicode字符編碼表中部分內容,左側4開頭的如“4E05”就是unicode的十六進制表示形式,很明顯,通過對比,我們可以找到字符“上”對應unicode的十六進制就是“4e0a”。

在unicode中你可以看到有6個像“上”一樣的字符,第一個指的是簡體中文,第二個指的是香港繁體字,第三個是臺灣繁體字,第四個是日本字,這些字符都是看起來類似的,所以在unicode中統(tǒng)一都用“4e0a”來存儲,但是每一個不同編碼的字符為了區(qū)分又添加了類似“494F”這樣的標識,unicode編碼也是通過這種方式來完成與萬國編碼的轉換過程。
4. Python2兩種字符串類型的區(qū)別
# coding:gbk
# 解釋器已經切換到Python2
x = "上"
print(type(x)) # str類型
print([x, ]) # 以gbk編碼保存
print([x.decode('gbk'), ]) # 解碼后就是unicode,與下面保存的字符編碼一致
y = u'上' # 定義字符串的時候前面加"u"
print(type(y)) # unicode類型
print([y, ]) # 以unicode編碼保存
5. 字符編碼的保存與取值原理
我們以GBK編碼為例,保存一個字符“上”,那么保存的結果應該是與上圖unicode編碼中對應的GBK編碼“494F”相對應,但是,請看如下代碼,打印結果并不對應,這又是為何呢?
# coding:gbk
# 解釋器已經切換到Python2
x = "上"
print([x, ]) # 以gbk編碼保存
"""
輸出
['\xc9\xcf']
"""
GBK可以保存中文或者英文字符,假如我們要保存一串字符“你a好”,應該是使用8bit+8bit+8bit+8bit+8bit一共40個比特位來保存。這樣保存的時候沒有問題,但是取值卻成了問題,計算機并不知道從那一個比特位開始到哪一個比特位結束是第一個字符,所以這樣連在一起無法取值(韓愈《師說》中寫到:“句讀之不知”也是一樣的道理,這句話指的是不理解標點符號,那么自然不理解句子的開始和結束,也就無法讀懂一句話)。計算機在取值的時候也一定要有一個明確的開始和結束,所以,其實計算機在保存的時候并不是8個比特位都用來表示字符的,這也是GBK表面上是保存65535個漢字其實并沒有那么多,它只能保存3萬多個。GBK能表示兩種字符,分別是中文和英文,其實它的第一個字符是用來區(qū)分中文和英文的,所以要保存一個“你a好”字符串,應該是如下保存方式:
(1+7bit)+(1+7bit)+(1+7bit)+(1+7bit)+(1+7bit)
假如第一個比特位是1代表中文字符,那么接下來他就會先讀第一個字節(jié)第一個比特位代表中文,接下來把這個字節(jié)讀完了之后,需要再一次讀取第二個字節(jié)的第一個比特位,剛好第一個比特位也是1,那么他就把后面的7個比特位讀完了就能準確地讀出這個中文字符,同理,如果一個字符的第一個比特位是0,那么它代表一個英文字符,那么它只需要把這個字節(jié)的內容讀完就能準確地讀出這個英文字符。
計算機原本都是機器,它之所以智能都是源自于人類的智慧。
如果你使用GBK編碼保存而是UTF-8編碼去讀取,由于兩種編碼每個字符所占用的字節(jié)數(shù)不同,那么關于每個字符的第一個字節(jié)的第一個比特位的標識也就不一致,就可能會產生第一個字符沒有讀完而后面的比特位標識與前面剛讀過的字符標識不一致,那么每個字符都不能準確的讀出來,這就會產生亂碼。
既然已經清楚了它的存取原理那么現(xiàn)在我們再來看一下GBK編碼保存一個中文字符“上”的過程,如下圖所示。

這個過程主要分為四個階段:
- 先“上”這個字符轉化十六進制分別是“c”,“9”,“c”,“f”
- 把“c”,“9”,“c”,“f”分別轉化成二進制對應的01010這些東西
- 取出每個字節(jié)的第一個二進制位
- 把剩余的每個十六進制位對應的多個二進制位再次轉化成十六進制
所以你看到的結果和unicode字符編碼表無法對應是由于計算機的存取原理所導致的。
6. 字符編碼總結
最后再回到第二小節(jié)直接打印x會出現(xiàn)亂碼的問題,龜叔在轉化的時候按照你在內存保存的時候的字符編碼GBK來轉化,但是Pycharm默認使用的打印到屏幕的字符編碼是UTF-8(這個可以改,但是不建議你修改),所以直接在Pycharm打印x你會看到亂碼,如果是Windows系統(tǒng)的用戶,可以在Windows終端以Python2執(zhí)行代碼,他就不會出現(xiàn)亂碼,因為Windows平臺默認打印到終端的編碼也是GBK,如果是MacOS系統(tǒng)的用戶,在終端以Python2執(zhí)行代碼還是會出現(xiàn)亂碼,因為MacOS系統(tǒng)默認打印到到終端的編碼是和Pycharm一樣的UTF-8。
所以很多寫Python2程序的人都會在str前面加一個“u”(可能他們自己也不知道這是為什么),這就是希望能夠把所有的字符串按照unicode字符編碼來保存,這樣可以和任意編碼轉換。 有的人可能會有疑問,Python為什么要來兩種字符串類型,這不是給使用者徒增麻煩嗎?
你能想到的龜叔自然也能想到,這里有一點關于時間先后的問題。
Python語言寫于1989年,1991年Python2才正式誕生,unicode是1990年開始研發(fā),1994年才真正誕生,UTF-8的誕生就更加的晚了,所以最開始的Python解釋器一定是使用ASCII編碼,那時候還沒有unicode字符編碼。
我們花費了大量的篇幅講解了字符編碼,這里面有一定的理論知識,但需要你記住的結論非常少,其他的深入的東西沒有人會問你(詳細知道的也沒幾個人),在Python3中所有的字符串都是用unicode編碼來保存(不需要前面加“u”),字符串的數(shù)據(jù)類型也只有一個,就是str,只要是用unicode來保存的,那么所有的字符串在任何情況下都不會出現(xiàn)亂碼,在Python3中代碼示例如下:
# coding:gbk
# 解釋器已經切換到Python3
x = "上"
print(x)
# unicode===>編碼encode===>gbk
code_gbk = x.encode('gbk')
code_utf8 = x.encode('utf-8')
print(code_gbk)
print(code_utf8)
print(type(code_gbk))
print(type(code_utf8))
print(code_gbk.decode('gbk'))
print(code_utf8.decode('utf-8'))
"""
輸出:
上
b'\xc9\xcf'
b'\xe4\xb8\x8a'
<class 'bytes'>
<class 'bytes'>
上
上
"""
從Python3中打印x.encode('gbk') 的結果中你可以看到是:‘\xc9\xcf’,這正是Python2中的str類型的值,而在Python3是bytes類型,在Python2中則是str類型,他們數(shù)據(jù)類型雖然不一致,但是存儲的結果確是一致的,他們他們之間必然存在一種關聯(lián)就是:Python2中的str類型就是Python3的bytes類型,我們可以查看Python2的str類型的源碼,看到部分代碼如下圖所示:

編碼之后的結果數(shù)據(jù)類型是bytes,看起來像是字節(jié),其實是十六進制,計算機會自動把十六進制轉化成二進制,你可以把bytes類型等同于二進制去看待,網絡傳輸也是基于二進制來傳輸?shù)模阍谏暇W的過程中網速很慢的時候可以看到1b/s,這就是指的每秒傳輸一個字節(jié)。