7、Python入門____語法入門

前言


首先,本次立項項目只是為了做網(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

IronPythonJython類似,只不過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上是不行的,但是,在MacLinux上是可以的,方法是在.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、ornot運算。

  • 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ù)工作。

搞清楚了ASCIIUnicodeUTF-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)常遇到strbytes的互相轉(zhuǎn)換。為了避免亂碼問題,應(yīng)當(dāng)始終堅持使用UTF-8編碼對strbytes進行轉(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編碼:

Notepad++

如果.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)strbytes互相轉(zhuǎn)換時,需要指定編碼。最常用的編碼是UTF-8。Python當(dāng)然也支持其他編碼方式,比如把Unicode編碼成GB2312

>>> '中文'.encode('gb2312')
b'\xd6\xd0\xce\xc4'

但這種方式純屬自找麻煩,如果沒有特殊業(yè)務(wù)要求,請牢記僅使用UTF-8編碼。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,119評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,382評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 176,038評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,853評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 71,616評論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,112評論 1 323
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,192評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,355評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,869評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,727評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,928評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,467評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,165評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,570評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,813評論 1 282
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,585評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 47,892評論 2 372

推薦閱讀更多精彩內(nèi)容