前言
首先,本次立項項目只是為了做網(wǎng)絡(luò)爬蟲,本身不需要Python完整的知識鏈。因此,對于基本語法,我會浮光掠影,快速瀏覽并實驗,為的是直搗黃龍~
滴滴滴
Python語言介紹
Python
是一種計算機程序設(shè)計語言。你可能已經(jīng)聽說過很多種流行的編程語言,比如非常難學(xué)的C
語言,非常流行的Java
語言,適合初學(xué)者的Basic
語言,適合網(wǎng)頁編程的JavaScript
語言等等。
那Python是一種什么語言?
首先,我們普及一下編程語言的基礎(chǔ)知識。用任何編程語言來開發(fā)程序,都是為了讓計算機干活,比如下載一個MP3,編寫一個文檔等等,而計算機干活的CPU只認識機器指令,所以,盡管不同的編程語言差異極大,最后都得“翻譯”成CPU可以執(zhí)行的機器指令。而不同的編程語言,干同一個活,編寫的代碼量,差距也很大。
比如,完成同一個任務(wù),C語言要寫1000行代碼,Java只需要寫100行,而Python可能只要20行。
所以Python是一種相當(dāng)高級的語言。
你也許會問,代碼少還不好?代碼少的代價是運行速度慢,C程序運行1秒鐘,Java程序可能需要2秒,而Python程序可能就需要10秒。
那是不是越低級的程序越難學(xué),越高級的程序越簡單?表面上來說,是的,但是,在非常高的抽象計算中,高級的Python程序設(shè)計也是非常難學(xué)的,所以,高級程序語言不等于簡單。
但是,對于初學(xué)者和完成普通任務(wù),Python語言是非常簡單易用的。連Google都在大規(guī)模使用Python,你就不用擔(dān)心學(xué)了會沒用。
用Python可以做什么?可以做日常任務(wù),比如自動備份你的MP3;可以做網(wǎng)站,很多著名的網(wǎng)站包括YouTube就是Python寫的;可以做網(wǎng)絡(luò)游戲的后臺,很多在線游戲的后臺都是Python開發(fā)的。總之就是能干很多很多事啦。
Python當(dāng)然也有不能干的事情,比如寫操作系統(tǒng),這個只能用C語言寫;寫手機應(yīng)用,只能用Swift/Objective-C(針對iPhone)和Java(針對Android);寫3D游戲,最好用C或C++。
Python簡介
現(xiàn)在,全世界差不多有600多種編程語言,但流行的編程語言也就那么20來種。如果你聽說過TIOBE排行榜,你就能知道編程語言的大致流行程度。這是最近10年最常用的10種編程語言的變化圖:
總的來說,這幾種編程語言各有千秋。C語言是可以用來編寫操作系統(tǒng)的貼近硬件的語言,所以,C語言適合開發(fā)那些追求運行速度、充分發(fā)揮硬件性能的程序。而Python是用來編寫應(yīng)用程序的高級編程語言。
當(dāng)你用一種語言開始作真正的軟件開發(fā)時,你除了編寫代碼外,還需要很多基本的已經(jīng)寫好的現(xiàn)成的東西,來幫助你加快開發(fā)進度。比如說,要編寫一個電子郵件客戶端,如果先從最底層開始編寫網(wǎng)絡(luò)協(xié)議相關(guān)的代碼,那估計一年半載也開發(fā)不出來。高級編程語言通常都會提供一個比較完善的基礎(chǔ)代碼庫,讓你能直接調(diào)用,比如,針對電子郵件協(xié)議的SMTP庫,針對桌面環(huán)境的GUI庫,在這些已有的代碼庫的基礎(chǔ)上開發(fā),一個電子郵件客戶端幾天就能開發(fā)出來。
Python就為我們提供了非常完善的基礎(chǔ)代碼庫
,覆蓋了網(wǎng)絡(luò)
、文件
、GUI
、數(shù)據(jù)庫
、文本
等大量內(nèi)容,被形象地稱作“內(nèi)置電池(batteries included)”。用Python開發(fā),許多功能不必從零編寫,直接使用現(xiàn)成的即可。
除了內(nèi)置的庫
外,Python還有大量的第三方庫,也就是別人開發(fā)的,供你直接使用的東西。當(dāng)然,如果你開發(fā)的代碼通過很好的封裝,也可以作為第三方庫給別人使用。
許多大型網(wǎng)站就是用Python開發(fā)的,例如YouTube、Instagram,還有國內(nèi)的豆瓣。很多大公司,包括Google、Yahoo等,甚至NASA(美國航空航天局)都大量地使用Python。
那Python適合開發(fā)哪些類型的應(yīng)用呢?
首選是網(wǎng)絡(luò)應(yīng)用,包括網(wǎng)站、后臺服務(wù)等等;
其次是許多日常需要的小工具,包括系統(tǒng)管理員需要的腳本任務(wù)等等;
另外就是把其他語言開發(fā)的程序再包裝起來,方便使用。
最后說說Python的缺點。
任何編程語言都有缺點,Python也不例外。優(yōu)點說過了,那Python有哪些缺點呢?
- 第一個缺點就是運行速度慢,和C程序相比非常慢,因為Python是解釋型語言,你的代碼在執(zhí)行時會一行一行地翻譯成CPU能理解的機器碼,這個翻譯過程非常耗時,所以很慢。而C程序是運行前直接編譯成CPU能執(zhí)行的機器碼,所以非常快。
但是大量的應(yīng)用程序不需要這么快的運行速度,因為用戶根本感覺不出來。例如開發(fā)一個下載MP3的網(wǎng)絡(luò)應(yīng)用程序,C程序的運行時間需要0.001秒,而Python程序的運行時間需要0.1秒,慢了100倍,但由于網(wǎng)絡(luò)更慢,需要等待1秒,你想,用戶能感覺到1.001秒和1.1秒的區(qū)別嗎?這就好比F1賽車和普通的出租車在北京三環(huán)路上行駛的道理一樣,雖然F1賽車理論時速高達400公里,但由于三環(huán)路堵車的時速只有20公里,因此,作為乘客,你感覺的時速永遠是20公里。
- 第二個缺點就是代碼不能加密。如果要發(fā)布你的Python程序,實際上就是發(fā)布源代碼,這一點跟C語言不同,C語言不用發(fā)布源代碼,只需要把編譯后的機器碼(也就是你在Windows上常見的xxx.exe文件)發(fā)布出去。要從機器碼反推出C代碼是不可能的,所以,凡是編譯型的語言,都沒有這個問題,而解釋型的語言,則必須把源碼發(fā)布出去。
這個缺點僅限于你要編寫的軟件需要賣給別人掙錢的時候。好消息是目前的互聯(lián)網(wǎng)時代,靠賣軟件授權(quán)的商業(yè)模式越來越少了,靠網(wǎng)站和移動應(yīng)用賣服務(wù)的模式越來越多了,后一種模式不需要把源碼給別人。
再說了,現(xiàn)在如火如荼的開源運動和互聯(lián)網(wǎng)自由開放的精神是一致的,互聯(lián)網(wǎng)上有無數(shù)非常優(yōu)秀的像Linux
一樣的開源代碼,我們千萬不要高估自己寫的代碼真的有非常大的“商業(yè)價值”。那些大公司的代碼不愿意開放的更重要的原因是代碼寫得太爛了,一旦開源,就沒人敢用他們的產(chǎn)品了。
安裝Python
因為Python是跨平臺的,它可以運行在Windows、Mac和各種Linux/Unix系統(tǒng)上。在Windows上寫Python程序,放到Linux上也是能夠運行的。
要開始學(xué)習(xí)Python編程,首先就得把Python安裝到你的電腦里。安裝后,你會得到Python解釋器
(就是負責(zé)運行Python程序的),一個命令行交互環(huán)境,還有一個簡單的集成開發(fā)環(huán)境。
安裝Python 3.7
目前,Python有兩個版本,一個是2.x版,一個是3.x版,這兩個版本是不兼容的。由于3.x版越來越普及,我們的教程將以最新的Python 3.7版本為基礎(chǔ)。請確保你的電腦上安裝的Python版本是最新的3.7.x,這樣,你才能無痛學(xué)習(xí)這個教程。
- 在Windows上運行Python時,請先啟動命令行,然后運行python。
- 在Mac和Linux上運行Python時,請打開終端,然后運行python3。
Python解釋器
當(dāng)我們編寫Python
代碼時,我們得到的是一個包含Python代碼的以.py
為擴展名的文本文件。要運行代碼,就需要Python解釋器去執(zhí)行.py文件。
由于整個Python語言從規(guī)范到解釋器都是開源的,所以理論上,只要水平夠高,任何人都可以編寫Python解釋器來執(zhí)行Python代碼(當(dāng)然難度很大)。事實上,確實存在多種Python解釋器。
1、CPython
當(dāng)我們從Python官方網(wǎng)站下載并安裝好Python 3.x后,我們就直接獲得了一個官方版本的解釋器:CPython
。這個解釋器是用C語言開發(fā)的,所以叫CPython。在命令行下運行python就是啟動CPython解釋器。
CPython是使用最廣的Python解釋器。教程的所有代碼也都在CPython下執(zhí)行。
2、IPython
IPython
是基于CPython
之上的一個交互式解釋器
,也就是說,IPython只是在交互方式上有所增強,但是執(zhí)行Python代碼的功能和CPython是完全一樣的。好比很多國產(chǎn)瀏覽器雖然外觀不同,但內(nèi)核其實都是調(diào)用了IE。
CPython用>>>
作為提示符
IPython用In [序號]:
作為提示符。
3、PyPy
PyPy是另一個Python解釋器,它的目標是執(zhí)行速度。PyPy采用JIT
技術(shù),對Python代碼進行動態(tài)編譯
(注意不是解釋),所以可以顯著提高Python代碼的執(zhí)行速度。
絕大部分Python代碼都可以在PyPy下運行,但是PyPy和CPython有一些是不同的,這就導(dǎo)致相同的Python代碼在兩種解釋器下執(zhí)行可能會有不同的結(jié)果。如果你的代碼要放到PyPy下執(zhí)行,就需要了解PyPy和CPython的不同點。
4、Jython
Jython
是運行在Java平臺上的Python解釋器,可以直接把Python代碼編譯成Java
字節(jié)碼執(zhí)行。
5、IronPython
IronPython
和Jython
類似,只不過IronPython是運行在微軟.Net
平臺上的Python解釋器,可以直接把Python代碼編譯成.Net的字節(jié)碼。
小結(jié)
Python的解釋器很多,但使用最廣泛的還是CPython。
如果要和Java或.Net平臺交互,最好的辦法不是用Jython或IronPython,而是通過網(wǎng)絡(luò)調(diào)用來交互,確保各程序之間的獨立性。
語法快速入門
1. 打印輸出
如果要讓Python打印出指定的文字,可以用print()
函數(shù),然后把希望打印的文字用單引號
或者雙引號
括起來,但不能混用單引號和雙引號:
>>> print('hello, world')
hello, world
這種用單引號或者雙引號括起來的文本在程序中叫字符串
2. 直接運行py文件
有同學(xué)問,能不能像.exe
文件那樣直接運行.py
文件呢?在Windows上是不行的,但是,在Mac
和Linux
上是可以的,方法是在.py
文件的第一行
加上一個特殊的注釋
:
#!/usr/bin/env python3
print('hello, world')
然后,通過命令給hello.py
以執(zhí)行權(quán)限:
$ chmod a+x hello.py
就可以直接運行hello.py了,比如在Mac下運行:
3. 輸出
print()
函數(shù)也可以接受多個字符串,用逗號“,”隔開,就可以連成一串輸出:
>>> print('The quick brown fox', 'jumps over', 'the lazy dog')
The quick brown fox jumps over the lazy dog
4. 輸入
現(xiàn)在,你已經(jīng)可以用print()輸出你想要的結(jié)果了。但是,如果要讓用戶從電腦輸入一些字符怎么辦?Python提供了一個input(),可以讓用戶輸入字符串,并存放到一個變量里。比如輸入用戶的名字:
>>> name = input()
Michael
print(name)
但是程序運行的時候,沒有任何提示信息告訴用戶:“嘿,趕緊輸入你的名字”,這樣顯得很不友好。幸好,input()可以讓你顯示一個字符串來提示用戶,于是我們把代碼改成:
name = input('please enter your name: ')
print('hello,', name)
再次運行這個程序,你會發(fā)現(xiàn),程序一運行,會首先打印出please enter your name:,這樣,用戶就可以根據(jù)提示,輸入名字后,得到hello, xxx的輸出:
C:\Workspace> python hello.py
please enter your name: Michael
hello, Michael
輸入是Input,輸出是Output,因此,我們把輸入輸出統(tǒng)稱為Input/Output
,或者簡寫為IO
。
input()
和print()
是在命令行下面最基本的輸入和輸出,但是,用戶也可以通過其他更高級的圖形界面完成輸入和輸出,比如,在網(wǎng)頁上的一個文本框輸入自己的名字,點擊“確定”后在網(wǎng)頁上看到輸出信息。
5. Python基礎(chǔ)
Python是一種計算機編程語言。計算機編程語言和我們?nèi)粘J褂玫淖匀徽Z言有所不同,最大的區(qū)別就是,自然語言在不同的語境下有不同的理解,而計算機要根據(jù)編程語言執(zhí)行任務(wù),就必須保證編程語言寫出的程序決不能有歧義,所以,任何一種編程語言都有自己的一套語法,編譯器或者解釋器就是負責(zé)把符合語法的程序代碼轉(zhuǎn)換成CPU能夠執(zhí)行的機器碼,然后執(zhí)行。Python也不例外。
Python的語法比較簡單,采用縮進方式,寫出來的代碼就像下面的樣子:
# print absolute value of an integer:
a = 100
if a >= 0:
print(a)
else:
print(-a)
以#
開頭的語句是注釋
,注釋是給人看的,可以是任意內(nèi)容,解釋器會忽略掉注釋。其他每一行都是一個語句,當(dāng)語句以冒號:
結(jié)尾時,縮進的語句視為代碼塊。
縮進有利有弊。好處是強迫你寫出格式化的代碼,但沒有規(guī)定縮進是幾個空格還是Tab。按照約定俗成的管理,應(yīng)該始終堅持使用4個空格的縮進。
縮進的另一個好處是強迫你寫出縮進較少的代碼,你會傾向于把一段很長的代碼拆分成若干函數(shù),從而得到縮進較少的代碼。
縮進的壞處就是“復(fù)制-粘貼”功能失效了,這是最坑爹的地方。當(dāng)你重構(gòu)代碼時,粘貼過去的代碼必須重新檢查縮進是否正確。此外,IDE很難像格式化Java代碼那樣格式化Python代碼
。
最后,請務(wù)必注意,Python程序是大小寫敏感的,如果寫錯了大小寫,程序會報錯。
小結(jié)
Python使用縮進來組織代碼塊,請務(wù)必遵守約定俗成的習(xí)慣,堅持使用4個空格的縮進。
在文本編輯器中,需要設(shè)置把Tab自動轉(zhuǎn)換為4個空格,確保不混用Tab和空格。
6. 數(shù)據(jù)類型
整數(shù)
Python可以處理任意大小的整數(shù),當(dāng)然包括負整數(shù),在程序中的表示方法和數(shù)學(xué)上的寫法一模一樣,例如:1,100,-8080,0,等等。
計算機由于使用二進制,所以,有時候用十六進制
表示整數(shù)比較方便,十六進制用0x
前綴和0-9,a-f表示,例如:0xff00,0xa5b4c3d2,等等。
浮點數(shù)
浮點數(shù)也就是小數(shù),之所以稱為浮點數(shù),是因為按照科學(xué)記數(shù)法表示時,一個浮點數(shù)的小數(shù)點位置是可變的,比如,1.23x109和12.3x108是完全相等的。
浮點數(shù)可以用數(shù)學(xué)寫法,如1.23,3.14,-9.01,等等。
但是對于很大或很小的浮點數(shù),就必須用科學(xué)計數(shù)法表示,把10用e替代,1.23x109就是1.23e9
,或者12.3e8
,0.000012可以寫成1.2e-5
,等等。
字符串
字符串是以單引號'
或雙引號"
括起來的任意文本,比如'abc',"xyz"等等。
請注意,''
或""
本身只是一種表示方式,不是字符串的一部分,因此,字符串'abc'
只有a,b,c這3個字符。如果'
本身也是一個字符,那就可以用""
括起來,比如"I'm OK"
包含的字符是I,',m,空格,O,K這6個字符。
如果字符串內(nèi)部既包含'
又包含"
怎么辦?可以用轉(zhuǎn)義字符\
來標識,比如:
轉(zhuǎn)義字符\可以轉(zhuǎn)義很多字符,比如\n
表示換行,\t
表示制表符,字符\
本身也要轉(zhuǎn)義,所以\\
表示的字符就是\
,可以在Python的交互式命令行用print()打印字符串看看:
>>> print('I\'m ok.')
I'm ok.
>>> print('I\'m learning\nPython.')
I'm learning
Python.
>>> print('\\\n\\')
\
\
多行字符串'''...'''還可以在前面加上r使用:
# -*- coding: utf-8 -*-
print('''line1
line2
line3''')
布爾值
布爾值和布爾代數(shù)的表示完全一致,一個布爾值只有True
、False
兩種值,要么是True,要么是False,在Python中,可以直接用True
、False
表示布爾值(請注意大小寫),也可以通過布爾運算計算出來:
布爾值可以用and
、or
和not
運算。
and運算是與運算,只有所有都為True,and運算結(jié)果才是True:
or運算是或運算,只要其中有一個為True,or運算結(jié)果就是True:
not運算是非運算,它是一個單目運算符,把True變成False,F(xiàn)alse變成True:
布爾值經(jīng)常用在條件判斷中,比如:
if age >= 18:
print('adult')
else:
print('teenager')
空值
空值是Python里一個特殊的值,用None
表示。None不能理解為0,因為0是有意義的,而None是一個特殊的空值。
此外,Python還提供了列表
、字典
等多種數(shù)據(jù)類型,還允許創(chuàng)建自定義數(shù)據(jù)類型。
變量
變量的概念基本上和初中代數(shù)的方程變量是一致的,只是在計算機程序中,變量不僅可以是數(shù)字,還可以是任意數(shù)據(jù)類型。
在Python中,等號
=
是賦值語句,可以把任意數(shù)據(jù)類型賦值給變量,同一個變量可以反復(fù)賦值,而且可以是不同類型的變量
例如:
# -*- coding: utf-8 -*-
a = 123 # a是整數(shù)
print(a)
a = 'ABC' # a變?yōu)樽址?print(a)
最后,理解變量在計算機內(nèi)存中的表示也非常重要。當(dāng)我們寫:
a = 'ABC'
此時,Python解釋器干了兩件事情:
在內(nèi)存中創(chuàng)建了一個'ABC'的字符串;
在內(nèi)存中創(chuàng)建了一個名為a的變量,并把它指向'ABC'。
也可以把一個變量a賦值給另一個變量b,這個操作實際上是把變量b指向變量a所指向的數(shù)據(jù),例如下面的代碼:
# -*- coding: utf-8 -*-
a = 'ABC'
b = a
a = 'XYZ'
print(b)
最后打印出b的值:ABC
最后一行打印出變量b的內(nèi)容到底是'ABC'呢還是'XYZ'?如果從數(shù)學(xué)意義上理解,就會錯誤地得出b和a相同,也應(yīng)該是'XYZ',但實際上b的值是'ABC',讓我們一行一行地執(zhí)行代碼,就可以看到到底發(fā)生了什么事:
執(zhí)行a = 'ABC'
,解釋器創(chuàng)建了字符串'ABC'
和變量a
,并把a
指向'ABC'
:
執(zhí)行b = a
,解釋器創(chuàng)建了變量b
,并把b
指向a
指向的字符串'ABC'
:
執(zhí)行a = 'XYZ'
,解釋器創(chuàng)建了字符串'XYZ'
,并把a
的指向改為'XYZ'
,但b
并沒有更改:
常量
所謂常量就是不能變的變量,比如常用的數(shù)學(xué)常數(shù)π就是一個常量。在Python中,通常用全部大寫的變量名表示常量:
PI = 3.14159265359
但事實上
PI
仍然是一個變量,Python根本沒有任何機制保證PI不會被改變,所以,用全部大寫的變量名表示常量只是一個習(xí)慣上的用法,如果你一定要改變變量PI的值,也沒人能攔住你。
最后解釋一下整數(shù)的除法為什么也是精確的。在Python中,有兩種除法,一種除法是/
:
>>> 10 / 3
3.3333333333333335
/
除法計算結(jié)果是浮點數(shù),即使是兩個整數(shù)恰好整除,結(jié)果也是浮點數(shù):
>>> 9 / 3
3.0
還有一種除法是//
,稱為地板除,兩個整數(shù)的除法仍然是整數(shù):
>>> 10 // 3
3
你沒有看錯,整數(shù)的地板除//
永遠是整數(shù),即使除不盡。要做精確的除法,使用/
就可以。
因為//
除法只取結(jié)果的整數(shù)部分,所以Python還提供一個余數(shù)運算,可以得到兩個整數(shù)相除的余數(shù):
>>> 10 % 3
1
無論整數(shù)做//
除法還是取余數(shù)
,結(jié)果永遠是整數(shù),所以,整數(shù)運算結(jié)果永遠是精確的。
小結(jié)
Python支持多種數(shù)據(jù)類型,在計算機內(nèi)部,可以把任何數(shù)據(jù)都看成一個“對象”,而變量就是在程序中用來指向這些數(shù)據(jù)對象的,對變量賦值就是把數(shù)據(jù)和變量給關(guān)聯(lián)起來。
對變量賦值x = y
是把變量x指向真正的對象,該對象是變量y所指向的。隨后對變量y的賦值不影響變量x的指向。
注意:Python的整數(shù)沒有大小限制,而某些語言的整數(shù)根據(jù)其存儲長度是有大小限制的,例如Java對32位整數(shù)的范圍限制在-2147483648-2147483647。
Python的浮點數(shù)也沒有大小限制,但是超出一定范圍就直接表示為 inf
(無限大)。
7.字符編碼
我們已經(jīng)講過了,字符串也是一種數(shù)據(jù)類型,但是,字符串比較特殊的是還有一個編碼問題
。
因為計算機只能處理數(shù)字,如果要處理文本,就必須先把文本轉(zhuǎn)換為數(shù)字才能處理。最早的計算機在設(shè)計時采用8
個比特(bit)作為一個字節(jié)(byte),所以,一個字節(jié)能表示的最大的整數(shù)就是255(二進制11111111=十進制255),如果要表示更大的整數(shù),就必須用更多的字節(jié)。比如兩個字節(jié)可以表示的最大整數(shù)是65535,4個字節(jié)可以表示的最大整數(shù)是4294967295。
由于計算機是美國人發(fā)明的,因此,最早只有127
個字符被編碼到計算機里,也就是大小寫英文字母、數(shù)字和一些符號,這個編碼表被稱為ASCII
編碼,比如大寫字母A的編碼是65,小寫字母z的編碼是122。
但是要處理中文顯然一個字節(jié)是不夠的,至少需要兩個字節(jié),而且還不能和ASCII編碼沖突,所以,中國制定了GB2312
編碼,用來把中文編進去。
你可以想得到的是,全世界有上百種語言,日本把日文編到Shift_JIS里,韓國把韓文編到Euc-kr里,各國有各國的標準,就會不可避免地出現(xiàn)沖突,結(jié)果就是,在多語言混合的文本中,顯示出來會有亂碼。
因此,Unicode
應(yīng)運而生。Unicode把所有語言都統(tǒng)一到一套編碼里,這樣就不會再有亂碼問題了。
Unicode標準也在不斷發(fā)展,但最常用的是用兩個字節(jié)
表示一個字符
(如果要用到非常偏僻的字符,就需要4個字節(jié))。現(xiàn)代操作系統(tǒng)和大多數(shù)編程語言都直接支持Unicode。
現(xiàn)在,捋一捋ASCII編碼
和Unicode編碼
的區(qū)別:
- ASCII編碼是1個字節(jié)
- Unicode編碼通常是2個字節(jié)。
字母A
用ASCII編碼是十進制的65
,二進制的01000001
;
字符0
用ASCII編碼是十進制的48
,二進制的00110000
,注意字符'0'
和整數(shù)0
是不同的;
漢字中
已經(jīng)超出了ASCII編碼的范圍,用Unicode編碼是十進制的20013,二進制的01001110 00101101。
你可以猜測,如果把ASCII編碼的A用Unicode編碼,只需要在前面補0就可以,因此,A
的Unicode編碼是00000000 01000001
。
新的問題又出現(xiàn)了:如果統(tǒng)一成Unicode編碼,亂碼問題從此消失了。但是,如果你寫的文本基本上全部是英文的話,用Unicode編碼比ASCII編碼需要多一倍的存儲空間,在存儲和傳輸上就十分不劃算。
所以,本著節(jié)約的精神,又出現(xiàn)了把Unicode編碼
轉(zhuǎn)化為“可變長編碼”
的UTF-8編碼
。UTF-8編碼把一個Unicode字符
根據(jù)不同的數(shù)字大小編碼成1-6個字節(jié),常用的英文字母被編碼成1個字節(jié),漢字通常是3個字節(jié),只有很生僻的字符才會被編碼成4-6個字節(jié)。如果你要傳輸?shù)奈谋景罅坑⑽淖址?,用UTF-8編碼就能節(jié)省空間:
字符 | ASCII | Unicode | UTF-8 |
---|---|---|---|
A | 01000001 | 00000000 01000001 | 01000001 |
中 | x | 0100111000101101 | 11100100 10111000 10101101 |
從上面的表格還可以發(fā)現(xiàn),UTF-8編碼
有一個額外的好處,就是ASCII編碼實際上可以被看成是UTF-8編碼的一部分,所以,大量只支持ASCII編碼的歷史遺留軟件可以在UTF-8編碼下繼續(xù)工作。
搞清楚了ASCII
、Unicode
和UTF-8
的關(guān)系,我們就可以總結(jié)一下現(xiàn)在計算機系統(tǒng)通用的字符編碼工作方式:
在計算機內(nèi)存中,統(tǒng)一使用
Unicode
編碼,當(dāng)需要保存到硬盤
或者需要傳輸
的時候,就轉(zhuǎn)換為UTF-8
編碼。
用記事本編輯的時候,從文件讀取的UTF-8
字符被轉(zhuǎn)換為Unicode
字符到內(nèi)存
里,編輯完成后,保存的時候再把Unicode
轉(zhuǎn)換為UTF-8
保存到文件
:
瀏覽網(wǎng)頁的時候,服務(wù)器會把動態(tài)生成的Unicode
內(nèi)容轉(zhuǎn)換為UTF-8
再傳輸?shù)綖g覽器:
所以你看到很多網(wǎng)頁的源碼上會有類似<meta charset="UTF-8" />
的信息,表示該網(wǎng)頁正是用的UTF-8編碼。
Python的字符串
搞清楚了令人頭疼的字符編碼問題后,我們再來研究Python
的字符串。
在最新的Python 3
版本中,字符串是以Unicode
編碼的,也就是說,Python的字符串支持多語言,例如:
>>> print('包含中文的str')
包含中文的str
對于單個字符的編碼,Python提供了
ord()
函數(shù)獲取字符的整數(shù)表示,chr()
函數(shù)把編碼轉(zhuǎn)換為對應(yīng)的字符:
>>> ord('A')
65
>>> ord('中')
20013
>>> chr(66)
'B'
>>> chr(25991)
'文'
如果知道字符的整數(shù)編碼,還可以用十六進制這么寫str:
>>> '\u4e2d\u6587'
'中文'
兩種寫法完全是等價的。
由于Python
的字符串類型是str
,在內(nèi)存中以Unicode
表示,一個字符對應(yīng)若干個字節(jié)。如果要在網(wǎng)絡(luò)上傳輸
,或者保存到磁盤
上,就需要把str
變?yōu)橐?code>字節(jié)為單位的bytes
。
要注意區(qū)分'ABC'
和b'ABC'
,前者是str
,后者雖然內(nèi)容顯示得和前者一樣,但bytes
的每個字符都只占用一個字節(jié)。
以Unicode
表示的str
通過encode()
方法可以編碼為指定的bytes
,例如:
>>> 'ABC'.encode('ascii')
b'ABC'
>>> '中文'.encode('utf-8')
b'\xe4\xb8\xad\xe6\x96\x87'
>>> '中文'.encode('ascii')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128)
反過來,如果我們從網(wǎng)絡(luò)
或磁盤上
讀取了字節(jié)流
,那么讀到的數(shù)據(jù)就是bytes
。要把bytes
變?yōu)?code>str,就需要用decode()
方法:
>>> b'ABC'.decode('ascii')
'ABC'
>>> b'\xe4\xb8\xad\xe6\x96\x87'.decode('utf-8')
'中文'
如果bytes
中包含無法解碼的字節(jié),decode()
方法會報錯:
>>> b'\xe4\xb8\xad\xff'.decode('utf-8')
Traceback (most recent call last):
...
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xff in position 3: invalid start byte
要計算str
包含多少個字符,可以用len()
函數(shù):
>>> len('ABC')
3
>>> len('中文')
2
len()
函數(shù)計算的是str
的字符數(shù)
,如果換成bytes
,len()
函數(shù)就計算字節(jié)數(shù):
>>> len(b'ABC')
3
>>> len(b'\xe4\xb8\xad\xe6\x96\x87')
6
>>> len('中文'.encode('utf-8'))
6
可見,1
個中文字符經(jīng)過UTF-8
編碼后通常會占用3
個字節(jié),而1個英文字符只占用1
個字節(jié)。
在操作字符串時,我們經(jīng)常遇到str
和bytes
的互相轉(zhuǎn)換。為了避免亂碼問題,應(yīng)當(dāng)始終堅持使用UTF-8
編碼對str
和bytes
進行轉(zhuǎn)換。
由于Python源代碼也是一個文本文件,所以,當(dāng)你的源代碼中包含中文的時候,在保存源代碼時,就需要務(wù)必指定保存為UTF-8
編碼。當(dāng)Python解釋器讀取源代碼時,為了讓它按UTF-8
編碼讀取,我們通常在文件開頭寫上這兩行:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
第一行注釋是為了告訴Linux/OS X
系統(tǒng),這是一個Python可執(zhí)行程序
,Windows系統(tǒng)會忽略這個注釋;
第二行注釋是為了告訴Python解釋器
,按照UTF-8
編碼讀取源代碼,否則,你在源代碼中寫的中文輸出可能會有亂碼。
申明了UTF-8
編碼并不意味著你的.py
文件就是UTF-8
編碼的,必須并且要確保文本編輯器正在使用UTF-8 without BOM
編碼:
如果.py
文件本身使用UTF-8
編碼,并且也申明了# -*- coding: utf-8 -*-
,打開命令提示符測試就可以正常顯示中文:
格式化
最后一個常見的問題是如何輸出格式化的字符串。我們經(jīng)常會輸出類似'親愛的xxx你好!你xx月的話費是xx,余額是xx'之類的字符串,而xxx的內(nèi)容都是根據(jù)變量變化的,所以,需要一種簡便的格式化字符串的方式。
在Python中,采用的格式化方式和C語言是一致的,用%
實現(xiàn),舉例如下:
>>> 'Hello, %s' % 'world'
'Hello, world'
>>> 'Hi, %s, you have $%d.' % ('Michael', 1000000)
'Hi, Michael, you have $1000000.'
你可能猜到了,%
運算符就是用來格式化字符串的。在字符串內(nèi)部
,%s
表示用字符串替換,%d
表示用整數(shù)替換,有幾個%?占位符,后面就跟幾個變量或者值,順序要對應(yīng)好。如果只有一個%?,括號可以省略。
常見的占位符有:
占位符 | 替換內(nèi)容 |
---|---|
%d | 整數(shù) |
%f | 浮點數(shù) |
%s | 字符串 |
%x | 十六進制整數(shù) |
如果你不太確定應(yīng)該用什么,%s
永遠起作用,它會把任何數(shù)據(jù)類型轉(zhuǎn)換為字符串:
>>> 'Age: %s. Gender: %s' % (25, True)
'Age: 25. Gender: True'
有些時候,字符串里面的%是一個普通字符怎么辦?這個時候就需要轉(zhuǎn)義,用%%
來表示一個%
:
>>> 'growth rate: %d %%' % 7
'growth rate: 7 %'
當(dāng)str
和bytes
互相轉(zhuǎn)換時,需要指定編碼
。最常用的編碼是UTF-8
。Python當(dāng)然也支持其他編碼方式,比如把Unicode
編碼成GB2312
:
>>> '中文'.encode('gb2312')
b'\xd6\xd0\xce\xc4'
但這種方式純屬自找麻煩,如果沒有特殊業(yè)務(wù)要求,請牢記僅使用UTF-8
編碼。