本章主要內容
- 縮進和代碼塊構建
- 識別注釋
- 給變量賦值
- 對表達式求值
- 使用常見數據類型
- 獲取用戶輸入
- 選用正確的Python式編碼風格
本章介紹最基礎的Python知識,包括如何使用賦值和表達式、如何輸入數字或字符串、如何在代碼中標明注釋等。首先將介紹Python如何組織代碼塊,這與其他的所有主流語言都不一樣。
4.1 縮進和代碼塊構建
與其他大部分編程語言不一樣,Python使用空白符(whitespace)和縮進來標識代碼塊。也就是說,循環體、else條件從句之類的構成,都是由空白符來確定的。大部分編程語言都是使用某種大括號來標識代碼塊的。下面的C語言代碼將會計算9的階乘,結果保存在變量r中:
/* C語言代碼 */ int n, r; n = 9; r = 1; while (n > 0) { r *= n; n--; }
這里的while循環體是用大括號包圍起來的,也就是每次循環將要執行的代碼。如上面的代碼所示,為了能清晰地表達用途,代碼一般都會多少帶點縮進。但是寫成以下格式也是允許的:
/* 隨意縮進的C語言代碼 */ int n, r; n = 9; r = 1; while (n > 0) { r *= n; n--; }
雖然以上代碼非常難以閱讀,但仍然可以正確運行。
下面是Python的等價實現:
# Python代碼(贊?。?n = 9 r = 1 while n > 0: r = r * n ?--- Python還支持C風格的寫法r * = n n = n – 1 ?--- Python還支持C風格的寫法n - = 1
Python不用大括號標識代碼結構,而是用縮進本身來標識。上述最后兩行代碼就是while循環體,就是因為它們緊隨while語句,并且比while語句縮進一級。如果這兩行代碼沒做縮進,就不會構成while循環體。
采用縮進而非大括號來標識代碼結構,可能需要一些時間來習慣,但卻有明顯的好處。
- 不再可能有缺失或多余的大括號。再也不用一遍遍地翻看代碼,只為在底部找到與前面的左括號匹配的右括號。
- 代碼結構的外觀直觀反映了其實際結構,看一眼就可以輕松了解代碼的架構。
- Python的編碼風格能大致統一。換句話說,不太可能因為要看懂別人的古怪代碼而抓狂。所有人的代碼都很像是自己寫的。
可能大家的代碼已經堅持采用了縮進,所以這算不上是一大進步。如果用了IDLE,每行都會自動縮進。如果要回退縮進級別,只需要按下Backspace鍵即可。大多數編程用的編輯器和IDE(如Emacs、VIM和Eclipse)都提供了自動縮進功能。如果在提示符后輸入命令時,前面有一個或多個空格,那么Python解釋器會返回錯誤消息。這件事可能需要犯一兩次錯誤才會適應。
4.2 識別注釋
在大多數情況下,Python文件中符號#之后的任何內容都是注釋,將會被編譯器忽略。有一種情況明顯例外,即字符串中的#只是一個普通字符:
# 將5賦給x x = 5 x = 3 # 現在x成了3 x = "# This is not a comment"
Python代碼中經常會加入注釋。
4.3 變量和賦值
賦值是最常用的Python命令,用法也與其他編程語言很類似。下面用Python代碼新建變量x,并賦值為5:
x = 5
與很多其他計算機語言不同的是,Python既不需要聲明變量類型,也不需要在每行代碼后面添加結束符。代碼換行即表示結束,變量在首次被賦值時會自動創建。
{Python中的變量:是容器(bucket)還是標簽(label)?!}
在Python中“變量”這個名稱或許有點兒誤導性,應該叫“名稱”或“標簽”會更準確一些。但是,似乎所有人都習慣稱為“變量”了。無論叫什么名稱,都應該知道Python中的變量是如何工作的。
對變量的常見解釋就是存儲值的容器,有點兒像是個桶(bucket),當然這不算精確。對許多編程語言(如C語言)來說,這種解釋是合理的。
但是,Python中的變量不是容器,而是指向Python對象的標簽,對象位于解釋器的命名空間中。任意數量的標簽(或變量)可以指向同一個對象。當對象發生變化時,所有指向它的變量的值都會改變。
看過以下這段簡單的代碼,就能理解上述含義了:
>>> a = [1, 2, 3] >>> b = a >>> c = b >>> b[1] = 5 >>> print(a, b, c) [1, 5, 3] [1, 5, 3] [1, 5, 3]
如果將變量視為容器,以上結果就說不通了。改變了一個容器的內容,另外兩個容器不應該同時發生變化。但是,如果變量只是指向對象的標簽,就說得通了。3個標簽都指向同一個對象,若對象發生變化,則3個標簽都會反映出來。
如果變量指向的是常量或不可變值,上述區別就不是十分明顯了:
>>> a = 1 >>> b = a >>> c = b >>> b = 5 >>> print(a, b, c) 1 5 1
因為變量指向的對象無法改變,所以變量的表現與兩種解釋均符合。實際上在第3行代碼執行完畢后,a、b和c就全都指向了同一個不可更改的整數對象,其值為1。下一行代碼b =5則讓b指向整數對象5,但a和c的指向沒有變化。
Python變量可以被設為任何對象,而在C和許多其他語言中,變量只能存儲聲明過的類型的值。下面的Python代碼是完全合法的:
>>> x = "Hello" >>> print(x) Hello >>> x = 5 >>> print(x) 5
一開始x是指向字符串對象“Hello”的,然后又指向了整數對象5。當然,這種特性可能會遭到濫用,因為隨意讓同一個變量名先后指向不同的數據類型,可能會讓代碼變得難以理解。
新的賦值操作會覆蓋之前所有的賦值,del語句則會刪除變量。如果在刪除變量之后設法輸出該變量的內容,將會引發錯誤,效果就像從未創建過該變量一樣:
>>> x = 5 >>> print(x) 5 >>> del x >>> print(x) Traceback (most recent call last): File "<stdin>", line 1, in <module> NameError: name 'x' is not defined >>>
這里首先出現了跟蹤信息(traceback),當檢測到錯誤(稱為異常)時就會被打印。最后一行代碼顯示出檢測到了異常,在這里是x的NameError。在被刪除之后,x不再是有效的變量名了。因為在交互模式下只輸出了一行代碼,所以上述示例中只返回了“line 1, in <module>”的跟蹤信息。通常在錯誤發生時,會返回已有函數的完整動態調用層次信息。如果用了IDLE,返回的信息也是差不多的,可能會得到如下所示的代碼:
Traceback (most recent call last): File "<pyshell#3>", line 1, in <module> print(x) NameError: name 'x' is not defined
第14章將會更加詳細地介紹這種錯誤處理機制。在Python標準庫文檔中,列出了所有可能出現的異常及其引發原因。請使用索引來查找收到的某個異常的信息(如NameError)。
Python變量的名稱是區分大小寫的,可以包含字母、數字和下劃線,但必須以字母或下劃線開頭。關于創建Python式風格的變量名稱的更多內容,參見4.10節。
4.4 表達式
對于Python支持的算術表達式,多數讀者都很熟悉。以下代碼將會計算3和5的平均值,結果保存在變量z中:
x = 3 y = 5 z = (x + y) / 2
注意,只涉及整數的算術操作符并不一定返回整數。即便所有數值全是整數,除法運算(從Python 3開始)也會返回浮點數,所以小數部分不會被截斷。如果需要傳統的返回截斷整數的整除,可以換用操作符//。
Python使用的是標準的算術優先規則。如果上述最后一行代碼省略了圓括號,就會被計算為x +(y / 2)。
表達式不一定只是包含數值,字符串、布爾值和許多其他類型的對象都能以各種方式在表達式中使用。后面在用到時會更詳細地介紹。
動手題:變量和表達式 請在Python shell中創建一些變量。在變量名中放置空格、短線或其他非字母或數字的字符,看看會發生什么?再嘗試一些復雜的表達式,例如x = 2 + 4 * 5 - 6/3。用圓括號對數字進行各種不同形式的分組,看看運算結果與原來未分組時的表達式有何不同。
4.5 字符串
與其他大多數編程語言一樣,Python用雙引號標識字符串。以下代碼將字符串“Hello, World”賦給變量x:
x ="Hello, World"
反斜杠可用于將字符轉義,賦予字符特殊的含義。n表示換行符,t表示制表符,\表示反斜杠符本身。而”則是雙引號本身,而不是字符串的結束符:
x = "tThis string starts with a "tab"." x = "This string contains a single backslash(\)."
可以用單引號代替雙引號。以下兩行代碼的效果是一樣的:
x = "Hello, World" x = 'Hello, World'
它們的唯一區別是:在單引號標識的字符串中,不需要對雙引號字符加反斜杠;在雙引號標識的字符串中,也不需要對單引號字符加反斜杠:
x = "Don't need a backslash" x = 'Can't get by without a backslash' x = "Backslash your " character!" x = 'You can leave the " alone'
普通字符串不允許跨行。以下代碼將是無效的:
# 以下Python代碼將引發錯誤——不能讓1個字符串跨越2行 x = "This is a misguided attempt to put a newline into a string without using backslash-n"
但是Python支持用三重雙引號標識字符串,這樣字符串中不用反斜杠就能包含單引號和雙引號:
x = """Starting and ending a string with triple " characters permits embedded newlines、and the use of " and ' without backslashes"""
現在x包含了兩個“””之間的所有字符。可以用三重單引號‘’’來代替雙引號以達到同樣的效果。
Python為字符串處理提供了足夠的功能,第6章將會做專題討論。
4.6 數值
也許大家已經對其他編程語言的標準數值操作比較熟悉了,因此本書沒有用單獨的章節來介紹Python的數值處理能力。本節將會介紹Python數值的獨有特性,Python文檔中給出了全部可用的函數。
Python提供了4種數值:整數、浮點數、復數和布爾值。整數常量就是0、-11、+33、123456之類的整數值,并且范圍是無限的,大小僅受限于機器資源。浮點數可用小數點或科學計數法表示:3.14、-2E-8、2.718281828。浮點數的精度由底層硬件決定,但通常相當于C語言中的雙精度(64位)類型。復數受關注的程度可能不高,本節后面將會單獨討論。布爾值是True或False,除是字符串形式之外,效果與1和0相同。
Python的算術操作與C語言很類似。兩個整數的計算操作會生成一個整數,當然除法(/)除外,因為除法的結果會是浮點數。如果用了除號//,則結果會是經過截斷的整數。浮點數操作則總是會返回浮點數。下面是一些例子:
>>> 5 + 2 - 3 * 2 1 >>> 5 / 2 # 普通除法將返回浮點數 2.5 >>> 5 / 2.0 # 結果還是浮點數 2.5 >>> 5 // 2 # 用'//'整除將返回截斷后的整數值 2 >>> 30000000000 # 在很多編程語言中,整型是放不下的 30000000000 >>> 30000000000 * 3 90000000000 >>> 30000000000 * 3.0 90000000000.0 >>> 2.0e-8 # 科學計數法將返回浮點數 2e-08 >>> 3000000 * 3000000 9000000000000 >>> int(200.2) ?--- ? 200 >>> int(2e2) ?--- ? 200 >>> float(200) ?--- ? 200.0
這幾句代碼顯式在多個類型間轉換?,int函數會將浮點數截斷。
與C或JAVA相比,Python的數值有兩個優點:整數可為任意大小,兩個整數的除法結果是浮點數。
4.6.1 內置數值處理函數
Python提供了以下數值操作函數,作為其內核的一部分:
abs、divmod、float、hex、int、max、min、oct、pow、round
詳情參見官方文檔。
4.6.2 高級數值處理函數
Python沒有內置更高級的數值處理函數,例如三角函數、雙曲線三角函數,以及一些有用的常量,但它們都在標準模塊math中提供,稍后將會詳細介紹模塊。現在只要知道,必須在Python程序或交互式會話中執行以下語句,才能使用本節的數學函數,這就足夠了。
from math import *
math模塊提供了以下函數和常量:
acos、asin、atan、atan2、ceil、cos、cosh、e、exp、fabs、floor、fmod、frexp、hypot、ldexp、 log、log10、mod、pi、pow、sin、sinh、sqrt、tan、tanh
{:—}詳情參見官方文檔。
4.6.3 數值計算
由于受限于運算速度,基本安裝的Python不太適合執行密集型數值計算。但強大的Python擴展NumPy高效實現了很多高級的數值處理操作。NumPy重點實現的是數組操作,包括多維矩陣,以及快速傅里葉變換等更高級的函數。在SciPy官網中應該能夠找到NumPy或其鏈接。
4.6.4 復數
只要表達式帶有nj的形式,就會自動創建復數:n與Python的整數和浮點數形式相同,j當然就是標準的虛數表示法,等于-1的平方根。例如:
>>> (3+2j) (3+2j)
注意,當計算結果為復數時,Python會加上圓括號,表示顯示的是個對象值:
>>> 3 + 2j - (4+4j) (-1-2j) >>> (1+2j) * (3+4j) (-5+10j) >>> 1j * 1j (-1+0j)
計算j * j則會如愿返回-1,但結果仍然是Python復數型對象。復數永遠不會被自動轉換為等價的實數或整數對象。但可以用real和imag屬性輕松訪問到復數的實部和虛部。
>>> z = (3+5j) >>> z.real 3.0 >>> z.imag 5.0
注意,復數的實部和虛部總是以浮點數返回。
4.6.5 高級復數函數
大多數用戶都會認為,計算-1的平方根不該有結果,而是應該報錯,因此math模塊中的函數并不適用于復數,與其類似的復數函數是由cmath模塊提供的:
acos、acosh、asin、asinh、atan、atanh、cos、cosh、e、exp、log、log10、pi、sin、sinh、sqrt、 tan、tanh
為了能在代碼中清晰地標識出這些特殊用途的復數函數,避免與普通的同名函數產生沖突,最好的做法是先導入cmath模塊:
import cmath
然后在用到復數函數時,顯式地引用cmath包:
>>> import cmath >>> cmath.sqrt(-1) 1j
{盡量少用<module> import *!}
上述例子很好地說明了為什么應盡量減少import語句的from <module> import *的用法。如果用from形式先導入math模塊,再導入cmath模塊,那么cmath模塊中的函數將會覆蓋math模塊的同名函數。對于閱讀代碼的人來說,也要花費更多的精力來找出某個函數的來源。有一些模塊經過了明確設計,必須使用上例中的導入形式。
有關如何使用模塊和模塊名稱的更多詳細信息,參見第10章。
重點是要記住,在導入cmath模塊之后,幾乎就能對其他數值類型進行任何操作了。
動手題:字符串和數值操作 在Python shell中,創建一些字符串和數值型變量(整數、浮點數和復數)。體驗一下操作的結果,包括跨類型的操作。例如,能否讓字符串乘以整數,或者乘以浮點數或復數呢?接下來載入math模塊并測試一些函數,然后載入cmath模塊并執行同名函數。當載入cmath模塊后,對整數或浮點數調用其中的函數,會產生什么結果?怎樣才能讓math模塊的函數重新可用呢?
4.7 None值
除字符串、數值等標準類型之外,Python還有一種特殊的基本數據類型,它定義了名為None的特殊數據對象。顧名思義,None用于表示空值。在Python中,None會以各種方式存在。例如,Python中的“過程”,只是一個沒有顯式返回值的函數,這表示默認返回的是None。
在日常的Python編程中,None經常被用作占位符,用于指示數據結構中某個位置的數據將會是有意義的,即便該數據尚未被計算出來。檢測None是否存在十分簡單,因為在整個Python系統中只有1個None的實例,所有對None的引用都指向同一個對象,None只等價于它自身。
4.8 獲取用戶輸入
利用input()函數可以獲取用戶的輸入。input()函數可以帶一個字符串參數,作為顯示給用戶的提示信息:
>>> name = input("Name? ") Name? Jane >>> print(name) Jane >>> age = int(input("Age? ")) ?--- 將輸入的字符串轉換為整數 Age? 28 >>> print(age) 28 >>>
這種獲取用戶輸入的方法相當簡單。用戶輸入是以字符串的形式獲得的,所以要想用作數字,必須用int()或float()函數進行轉換。這算得上是個小陷阱吧。
動手題:獲取用戶輸入 體驗一下用input()函數讀取用戶輸入的字符串和整數。代碼與上述例子類似,如果讀取整數時沒有在input()外面調用int(),那會出現什么效果?能否修改一下代碼,讀取一個浮點數(如28.5)?如果故意輸入“錯誤”的數據類型會怎么樣?例如,本該是整數的地方輸入了浮點數,本該是數字的地方輸入了字符串,反之又會如何?
4.9 內置操作符
Python提供了多種內置操作符,標準的操作符有+、*等,更高級的有移位、按位邏輯運算函數等。大多數操作符都不是Python獨有的,其他的編程語言也提供,因此本書不再做解釋。Python內置操作符的完整列表,可在官方文檔中找到。
4.10 基本的Python編碼風格
除明確要求用縮進來標識代碼塊之外,Python對編碼風格的限制相對較少。即便如此,縮進量和縮進類型(制表符與空格)也沒做強制性規定。不過在“Python 增強提案 8”(Python Enhancement Proposal 8,PEP 8)中,包含了推薦的編碼風格規范。本書附錄A有對PEP 8的概括性介紹,全文可在Python官方網站在線獲取。表4-1中列出了部分Python式風格的規范,但為了能完全理解Python式風格,還請反復閱讀PEP 8。
強烈建議遵循PEP 8規范。因為每條規范都是精心挑選過的,并經過了時間考驗,能讓代碼更容易被Python程序員理解。
速測題:Python風格 請在以下變量名和函數名中,選出不大符合Python風格的名稱,并說明理由:bar(、varName、VERYLONGVARNAME、foobar、longvarname、foo_bar()、really_very_long_var_name。
4.11 小結
- 上面介紹的基礎語法已足夠開始寫Python代碼了。
- Python語法一目了然、始終如一。
- 由于語法沒有很多新奇之處,很多程序員的上手速度快得出奇。
本文摘自《Python 快速入門》第3版
- Python編程基礎教程從入門到實踐書籍
- 零基礎快速上手學Python核心編程
- Python軟件基金會作品,提供習題答案及源代碼
這是一本Python快速入門書,基于Python 3.6編寫。本書分為4部分,第一部分講解Python的基礎知識,對Python進行概要的介紹;第二部分介紹Python編程的重點,涉及列表、元組、集合、字符串、字典、流程控制、函數、模塊和作用域、文件系統、異常等內容;第三部分闡釋Python的特性,涉及類和面向對象、正則表達式、數據類型即對象、包、Python庫等內容;第四部分關注數據處理,涉及數據文件的處理、網絡數據、數據的保存和數據探索,最后給出了相關的案例。
本書框架結構清晰,內容編排合理,講解循序漸進,并結合大量示例和習題,讓讀者可以快速學習和掌握Python,既適合Python初學者學習,也適合作為專業程序員的簡明Python參考書。