- Python自動化運維快速入門
- 鄭征
- 6061字
- 2019-12-09 14:48:14
1.5 Python基礎語法
1.5.1 數字運算
編程是將問題數據化的一個過程,數據離不開數字,Python的數字運算規則與我們學習的四則運算規則是一樣的,即使不使用Python來編寫復雜的程序,也可以將其當作一個強大的計算器。打開Python,試運行以下命令:

提示
在不同的機器上浮點運算的結果可能會不一樣。
在整數除法中,除法(/)總是返回一個浮點數,如果只想得到整數的結果,就可以使用運算符//。整數除法返回浮點型,整數和浮點數混合運算的結果也是浮點型。

Python可以使用**操作來進行冪運算。

在交互模式中,最后被輸出的表達式結果被賦值給變量_,這樣能使后續計算更方便。例如:
>>> tax = 12.5 / 100 >>> price = 100.50 >>> price * tax 12.5625 >>> price + _ 113.0625 >>> round(_, 2) 113.06
Python數字類型轉換:
int(x)將x轉換為一個整數。
float(x)將x轉換為一個浮點數。
complex(x)將x轉換為一個復數,實數部分為x,虛數部分為0。
complex(x, y)將x和y轉換為一個復數,實數部分為x,虛數部分為y。x和y是數字表達式。
常用的數學函數可參見表1-2。
表1-2 常用的數學函數

1.5.2 字符串
1. 認識簡單字符串
Python中的字符串有幾種表達方式,可以使用單引號、雙引號或三引號(三個單引號或三個雙引號)括起來。例如:
>>> 'abc' 'abc' >>> "abc" 'abc' >>> '''a\ ... b\ ... c''' #使用 反斜線(\)來續行 'abc' >>> '''abc''' 'abc'
如果想要字符串含有單引號、雙引號該怎么處理呢?有兩種方法:一是使用反斜杠轉義引號;二是使用與字符串中單引號、雙引號不同的引號來定義字符串。例如:

使用\n換行或使用三引號。例如:

如果需要避免轉義,則可以使用原始字符串,即在字符串的前面加上r。例如:
>>> s = r"This is a rather long string containing\n\ ... several lines of text much as you would do in C." >>> print(s) This is a rather long string containing\n\ several lines of text much as you would do in C.
字符串可以使用+運算符連接在一起,或者使用*運算符重復字符串。例如:
>>> word = 'Help' + ' '+ 'ME' >>> print(word) Help ME >>> word="word "*5 >>> print(word) word word word word word
2. 字符串的索引
字符串可以被索引,就像C語言中的數組一樣,字符串的第一個字符的索引為0,一個字符就是長度為一的字符串。與Icon編程語言類似,子字符串可以使用分切符來指定:用冒號分隔的兩個索引,第一個索引默認為0,第二個索引默認為最后一個位置,s[:]表示整個字符串,s[2:3]表示從第3個字符開始,到第4個字符結束,不含第4個字符。不同于C字符串的是,Python字符串不能被改變。向一個索引位置賦值會導致錯誤,例如:

3. 字符串的遍歷
遍歷字符串有三種方式:一是使用enumerate函數,其返回字符串的索引及相應的字符;二是直接使用for循環;三是通過字符索引來遍歷。例如:

有一個方法可以幫我們記住分切索引的工作方式,想象索引是指向字符之間,第一個字符左邊的數字是0,接著有n個字符的字符串最后一個字符的右邊是索引n。例如:

如s[1:3]代表bc,s[-2:-1]代表f。
4. 字符串的格式化
Python支持格式化字符串的輸出。盡管這樣可能會用到非常復雜的表達式,但最基本的用法是將一個值插入到一個有字符串格式符%s的字符串中。
>>> print ("我叫 %s 今年 %d 歲!" % ('小明', 10))#使用% 我叫 小明 今年 10 歲! >>> print ("我叫 {} 今年 {} 歲!" .format('小明', 10))#使用字符串的format方法 我叫 小明 今年 10 歲! >>> print ("我叫 {0} 今年 {1} 歲!" .format('小明', 10,20))#使用索引,整數20未用到 我叫 小明 今年 10 歲!
需要在字符中使用特殊字符時,Python用反斜杠(\)轉義字符,如表1-3所示。
表1-3 轉義字符

5. 字符串的內建函數
Python字符串的內建函數可參見表1-4。
表1-4 字符串的內建函數

(續表)

1.5.3 列表與元組
列表是Python常用的數據類型,也是最基本的數據結構。Python的列表是由方括號“[]”[]括起,使用“,”分隔的序列,序列中的數據類型不要求一致,序列的索引從0開始。
【示例1-1】創建一個列表,只要把逗號分隔的不同數據項使用方括號括起來即可。
>>> list1 = ['Google', 'Huawei', 1997, 2000]; >>> list2 = [1, 2, 3, 4, 5 ]; >>> list3 = ["a", "b", "c", "d"]; >>> list4=["all of them",list1,list2,list3] >>> print ("list1[0]: ", list1[0]) list1[0]: Google >>> print ("list2[1:5]: ", list2[1:5]) list2[1:5]: [2, 3, 4, 5] >>> print(list4) ['all of them', ['Google', 'Huawei', 1997, 2000], [1, 2, 3, 4, 5], ['a', 'b', 'c', 'd']] >>> print(list4[1][1]) Huawei
【示例1-2】更新一個列表,可以對列表的數據項進行修改,也可以使用append()方法添加列表項。

【示例1-3】刪除列表中的某個元素。
>>> list = ['Google', 'Huawei', 1997, 2000] >>> del list[0] >>> print(list) ['Huawei', 1997, 2000]
列表還有一些其他操作,如列表對+和*的操作符與字符串相似,+號用于組合列表,*號用于重復列表。

列表的常用方法可參見表1-5。
表1-5 列表的常用方法

元組與列表類似,用“()”括起,“,”分隔的序列,不同于列表的是,元組是只讀的,無法被修改,在定義時其元素必須確定下來,也可以像列表一樣使用索引來訪問。
【示例1-4】元組的應用。

注意,元組元素不變是指元組每個元素指向永遠不變,如果元組的某個元素是一個列表,那么這個列表的元素是可以被改變的,但元組指向這個列表永遠不變。
【示例1-5】元組的某個元素是列表。

如果希望元組中的每個元素無法被修改,就必須保證元組的每一個元素本身也不能變,如數字、字符串、元組等不可變數據類型。
1.5.4 字典
一提到字典,我們就會想到中華字典、英語詞典等,通過給定的單詞(key)查找其含義(value)。在字典里,要查找的單詞(key)是唯一的,但不同的單詞其含義(value)可能相同。Python里的字典就是鍵值對(key-value)組成的集合,且可存儲任意類型對象。定義一個字典非常簡單:使用一對花括號{}括起,鍵值對之間使用“,”分隔。例如:

字典值可以是任何的Python對象,既可以是標準對象,也可以是用戶自定義的對象,但鍵不行。兩個重要的點需要記住:
(1)不允許同一個鍵出現兩次。創建時如果同一個鍵被賦值兩次,后一個值就會被記住。
【示例1-6】不允許同一個鍵出現兩次。
>>> dict = { 'hello':'你好','world':'世界','hello':'world'} #鍵hello的值被更新為 world >>> dict {'hello': 'world', 'world': '世界'}
(2)因為鍵必須不可變,所以可以用數字、字符串或元組充當,用列表則不行,即鍵必須為不可變數據類型。
【示例1-7】鍵必須為不可變數據類型。
>>> d = { 'a':1,'b':2, ['a']:'abc'} #鍵是列表,會報錯 Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unhashable type: 'list'
【示例1-8】遍歷字典。

【示例1-9】修改字典。
>>> d = { 'a':1, 'b':2, 'c':3, 'd':4, 'e':5, 'f':6 } >>> d['b']='b' >>> d {'a': 1, 'b': 'b', 'c': 3, 'd': 4, 'e': 5, 'f': 6}
【示例1-10】刪除字典元素。可以刪除單一的元素,也可以一次性刪除所有元素,清空字典,顯式地刪除一個字典用del命令。

Python字典的內置方法可參見表1-6。
表1-6 字典的常用方法

1.5.5 集合
集合set是一個無序不重復元素集,基本功能包括關系測試和消除重復元素。集合對象還支持union(聯合)、intersection(交)、difference(差)和sysmmetric difference(對稱差集)等數學運算。
在Python中可以使用“x in set”來判斷x是否在集合中,使用“len(set) ”來獲取集合元素個數,使用“for x in set”來遍歷集合中的元素。但由于集合不記錄元素位置,因此集合不支持獲取元素位置和切片等操作。
【示例1-11】集合的定義和常見用法。

【示例1-12】使用集去重元素。
>>> a = [11,22,33,44,11,22] >>> b = set(a) >>> b set([33, 11, 44, 22])
集合的基本操作可參見表1-7。
表1-7 集合的基本操作

提示
union()、intersection()、difference()和symmetric_difference()的非運算符(non-operator就是形如s.union()這樣的)版本將會接受任何可迭代對象(iterable)作為參數。相反,它們的運算符版本(&^+-|)要求參數必須是集合對象。
1.5.6 函數
在中學數學中我們知道y=f(x)代表著函數,x是自變量,y是函數f(x)的值。在程序中,自變量x可以代表任意的數據類型,可以是字符串、列表、字典、對象,可以是我們認為的任何東西。
【示例1-13】以簡單的數據計算函數為例,定義函數fun(a,b,h)來計算上底為a,下底為b,高為h的梯形面積。

函數的目的是封裝,提高應用的模塊性及代碼的重復利用率。將常用的處理過程寫成函數,在需要時調用它,可以屏蔽實現細節,減少代碼量,增加程序可讀性。
【示例1-14】假如多個梯形的面積需要計算,那么:

上例中的調用方法fun(3,4,5)并不直觀,為了增加可讀性,這里我們稍做調整。

在調用此函數傳遞參數時使用參數關鍵字,這樣參數的位置可以任意放置而不影響運算結果,增加程序可讀性。假如待計算的梯形默認高度均為5,就可以定義帶默認值參數的函數。

提示
帶有默認值的參數必須位于不含默認值參數的后面。
關于函數是否會改變傳入變量的值有以下兩種情況。
(1)對不可變數據類型的參數,函數無法改變其值,如Python標準數據類型中的字符串、數字、元組。
(2)對可變數據類型的參數,函數可以改變其值,如Python標準數據類型中的列表、字典、集合。
【示例1-15】舉例說明。

1.5.7 條件控制與循環語句
1. 條件控制
Python的條件控制是通過一條或多條語句的執行結果(True或False)來決定執行的代碼塊。條件控制的流程如圖1.26所示。

圖1.26 條件控制的流程
if語句的一般形式如下:

解釋:如果條件1為真,則執行語句1;如果條件1不為真,條件2為真,則執行語句2;如果條件1、條件2都不為真,則執行語句3。其中elif和else語句不是必需的。
【示例1-16】將下列代碼保存為lx_if.py。

在命令窗口執行python lx_if.py后得到如下結果。
99 excellent 80 fine 70 pass 60 pass 59 bad
if語句還可以用來實現問題表達式。例如:有整數變量a、b、c,如果a<b,那么c=a,否則c=b。我們可以用一行代碼實現:

2. 循環語句
Python有兩種方式來實現循環:while語句和for語句。
while語句的結構如下:

當條件判斷為真時執行語句1,當條件判斷為假時執行語句2,其實只要不是死循環,語句2就一定會被執行。因此,while語句的結構也可以如下:

while語句的流程如圖1.27所示。

圖1.27 while語句的流程
【示例1-17】將下面的代碼保存為lx_while.py。

在命令窗口中執行python lx_while.py,并嘗試輸入一些字符,結果如下。
please input something,'q' for quit.-> hello your input is hello please input something,'q' for quit.-> python your input is python please input something,'q' for quit.-> q your input is q You're out of circulation.
Python for循環可以遍歷任何序列的項目,如一個列表或一個字符串。for循環的一般格式如下:
for <variable> in <sequence>: <statements> else: <statements>
【示例1-18】計算1~1000的所有整數的和。

循環中的break語句和continue語句:從英文字面意思來理解即可,break就是中斷,跳出當前的循環,不再繼續執行循環內的所有語句;continue就是繼續,程序運行至continue處時,不再執行continue后的循環語句,立即進行下一次循環判斷。下面通過一個例子來了解兩者的區別。
【示例1-19】break語句和continue語句的比較(lx_break_continue.py)。

在命令行中運行python lx_break_continue.py將得到如下結果。
break-------------- aaa 0 bbb 1 aaa 1 continue-------------- aaa 0 bbb 1 aaa 1 aaa 2 bbb 3 aaa 3 bbb 4 aaa 4 bbb 5
我們看到break直接跳出了循環,而continue只是跳過了其中一步(輸出bbb 2的那一步)。
1.5.8 可迭代對象、迭代器和生成器
迭代是Python最強大的功能之一,是訪問集合元素的一種方式。迭代器是一個可以記住遍歷位置的對象。迭代器對象從集合的第一個元素開始訪問,直到所有的元素被訪問結束。迭代器只能往前不會后退。迭代器有兩個基本的方法:iter()和next()。字符串、列表或元組對象都可用于創建迭代器。
首先來了解一下可迭代對象、迭代器和生成器的概念。
(1)可迭代對象:如果一個對象擁有__iter__方法,這個對象就是一個可迭代對象。在Python中,我們經常使用for來對某個對象進行遍歷,此時被遍歷的對象就是可迭代對象,常見的有列表、元組、字典。for循環開始時自動調用可迭代對象的__iter__方法獲取一個迭代器,for循環時自動調用迭代器的next方法獲取下一個元素,當調用可迭代器對象的next方法引發StopIteration異常時,結束for循環。
(2)迭代器:如果一個對象擁有__iter__方法和__next__方法,這個對象就是一個迭代器。
(3)生成器:生成器是一類特殊的迭代器,就是在需要時才產生結果,而不是立即產生結果。這樣可以同時節省CPU和內存。有兩種方法可以實現生成器:
生成器函數。使用def定義函數,使用yield而不是return語句返回結果。yield語句一次返回一個結果,在每個結果中間掛起函數的狀態,以便下次從它離開的地方繼續執行。
生成器表達式。類似于列表推導,只不過是把一對大括號[]變換為一對小括號()。但是生成器表達式是按需產生一個生成器結果對象,要想拿到每一個元素,就需要循環遍歷。
三者之間的關系如圖1.28所示。

圖1.28 可迭代對象、迭代器和生成器的關系
可迭代對象包含迭代器、序列、字典;生成器是一種特殊的迭代器,下面分別舉例說明。
【示例1-20】創建一個迭代器對象(lx_iterator.py)。

因為類MyListIterator實現了__iter__方法和__next__方法,所以它是一個迭代器對象。由于__iter__方法本返的是迭代器(本身),因此它也是可迭代對象。迭代器必然是一個可迭代對象。
下面使用三種方法遍歷迭代器MyListIterator。

輸出結果如下:
使用for循環來遍歷迭代器 0 1 2 3 4 使用next來遍歷迭代器 0 1 2 3 4 同時使用next和for來遍歷迭代器 先使用兩次next 0 1 再使用for,會從第三個元素2開始輸出 2 3 4
從結果可以看出,for循環實際上就是調用了迭代器的__next__方法,當捕捉到MyListIterator異常時自動結束for循環。
【示例1-21】創建一個可迭代對象。

因為對象MyList實現了__iter__方法返回了迭代器類的實例,所以它是一個可迭代對象。遍歷操作可使用for循環,不可使用next()。for循環實質上還是調用MyListIterator的__next__方法。

輸出結果如下:
使用for循環來遍歷可迭代對象my_list 0 1 2 3 4 使用next來遍歷可迭代對象my_list print(next(my_list)) TypeError: 'MyList' object is not an iterator
從運行結果知道,可迭代對象如果沒有__next__方法,則無法通過next()進行遍歷。
【示例1-22】創建一個生成器,像定義一般函數一樣,只不過使用yield返回中間結果。生成器是一種特殊的迭代器,自動實現了迭代器協議,即__iter__方法和next方法,不需要再手動實現兩個方法。創建生成器:

遍歷生成器:

運行結果如下:
for 循環遍歷生成器myList 0 1 2 3 4 next遍歷生成器myList 0 1 2 3 4
具有yield關鍵字的函數都是生成器,yield可以理解為return,返回后面的值給調用者。不同的是return返回后,函數會釋放,而生成器則不會。在直接調用next方法或用for語句進行下一次迭代時,生成器會從yield下一句開始執行,直至遇到下一個yield。
1.5.9 對象賦值、淺復制、深復制
Python中對象的賦值,復制(深/淺復制)之間是有差異的,如果使用時不注意,就可能導致程序崩潰或嚴重bug。下面就通過簡單的例子來介紹這些概念之間的差別。
【示例1-23】對象賦值操作(testFuzhi.py)。

輸出結果如圖1.29所示。

圖1.29 對象賦值操作
下面來分析代碼:首先第3行創建了一個名為object1的變量,這個變量指向一個list對象,第5行將object1賦給object2,然后打印它們及它們指向的對象在內存中的地址(通過id函數)。第18和19行修改object1,然后分別打印object1與object2在內存中的地址。從運行結果來看,無論是object1還是object2,它們都向同一個內存地址,即指向的都是同一個對象,也就是說“object1 is object2 and object1[i] is object2[i] ”,對object1的操作同樣會反應到object2上,打印object1和object2的結果始終是顯示一致的。
【示例1-24】淺復制操作(testCopy.py)。

運行結果如圖1.30所示。

圖1.30 淺復制操作
代碼說明:與testFuzhi.py不同的是,第2行導入copy模塊,第5行調用copy模塊的copy函數來為object2進行賦值,也就是淺復制操作。從運行結果來看,object1與object2指向內存中的不同位置,它們屬于兩個不同的對象,但列表內部仍指向同一個位置。修改了object1[0]= "Wilber"后,object1對象的第一個元素指向了新的字符串常量"Wilber",而object2仍指向"Will"。執行object1[2].append("CSS")時object1[2]的地址并未改變,object1與object2的第三個元素仍指向此子列表。
總結一下淺復制:通過copy模塊中的淺復制函數copy()對object1指向的對象進行淺復制,然后淺復制生成的新對象賦值給object2變量。淺復制會創建一個新的對象,這個例子中"object1 is not object2",但是對于對象中的元素,淺復制就只會使用原始元素的引用(內存地址),也就是說,"wilber[i] is will[i]"。當對object1進行修改時由于list的第一個元素是不可變類型,因此object1對應的list的第一個元素會使用一個新的對象,但是list的第三個元素是一個可變類型,修改操作不會產生新的對象,object1的修改結果會就相應地反應到object2上。
【示例1-25】深復制操作(testDeepCopy.py)。

運行結果如圖1.31所示。

圖1.31 深復制操作
從運行結果來看,這個非常容易理解,就是創建了一個與之前對象完全獨立的對象。通過copy模塊中的深復制函數deepcopy()對object1指向的對象進行深復制,然后深復制生成的新對象賦值給object2變量。與淺復制類似,深復制也會創建一個新的對象,這個例子中"object1 is not object2",但是對于對象中的元素,深復制都會重新生成一份(有特殊情況,下面會說明),而不是簡單地使用原始元素的引用(內存地址)。也就是說," object1[i] is not object2[i]"。
復制有一些特殊情況:
對于原子數據類型(如數字、字符串、只含不可變數據類型的元組)沒有復制一說,賦值操作相當于產生一個新的對象,對原對象的修改不影響新對象。簡言之,賦值操作與淺復制和深復制的效果是一樣的。
如果元組變量只包含原子類型對象,深復制就不會重新生成對象,這其實是Python解釋器內部的一種優化機制,對于只包含原子類型對象的元組,如果它們的值相等,就在內存中保留一份,類似的還有小整數從-5~256。在內存中只保留一份,可節省內存,提高訪問速度,如圖1.32所示。

圖1.32 元組的深復制
- The Android Game Developer's Handbook
- Python數據分析入門與實戰
- 新一代通用視頻編碼H.266/VVC:原理、標準與實現
- C++面向對象程序設計(微課版)
- 區塊鏈架構與實現:Cosmos詳解
- Rust編程從入門到實戰
- 利用Python進行數據分析(原書第3版)
- 劍指MySQL:架構、調優與運維
- 精通MATLAB(第3版)
- SQL Server與JSP動態網站開發
- Spring+Spring MVC+MyBatis從零開始學
- 編程改變生活:用Python提升你的能力(進階篇·微課視頻版)
- OpenCV Android Programming By Example
- 遠方:兩位持續創業者的點滴思考
- HTML5移動Web開發