Python學習筆記:Python高階特性
Python學習筆記:Python高階特性
學自廖雪峰巨佬的Python3教程:
https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/0014317568446245b3e1c8837414168bcd2d485e553779e000
1.Mark下大佬的一句話:在Python中,程式碼不是越多越好,而是越少越好。程式碼不是越複雜越好,而是越簡單越好。1行程式碼能實現的功能,決不寫5行程式碼。請始終牢記,程式碼越少,開發效率越高。筆者概括起來就是:Less is More
2.切片:即取一個list或tuple的部分元素
笨方法:用迴圈取
機智的方法:使用Python提供的切片操作符
# 一個list L = ['Michael', 'Sarah', 'Tracy', 'Bob', 'Jack'] # 從索引0開始取,直到索引3為止,但不包括索引3,如果第一個索引是0,還可以忽略 >>> L[:3] ['Michael', 'Sarah', 'Tracy'] # 從索引1開始取到3 >>> L[1:3] ['Sarah', 'Tracy'] #從後開始取兩個 >>> L[-2:] ['Bob', 'Jack'] # 取倒數第二個 >>> L[-2:-1] ['Bob'] # 另一個list >>> L = list(range(100)) >>> L [0, 1, 2, 3, ..., 99] # 前10個數,每兩個取一個 >>> L[:10:2] [0, 2, 4, 6, 8] # 所有數,每5個取一個 >>> L[::5] [0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95] # 原樣複製一個list >>> L[:] [0, 1, 2, 3, ..., 99] # 對tuple切片 >>> (0, 1, 2, 3, 4, 5)[:3] (0, 1, 2) # 對字串切片 >>> 'ABCDEFG'[:3] 'ABC' >>> 'ABCDEFG'[::2] 'ACEG'
可以看到切片操作符簡直無所不能切,只是tuple切出來還是tuple,字串切完還是字串
非遞迴版本:
#!/usr/bin/env python # -*- coding: utf-8 -*- def trim(s): # 注意:用切片的時候,tail代表的那一位是不取的 head = 0 tail = len(s) # 如果字串長度為0,則直接返回空 if tail == 0: return '' # 第一個迴圈裡的head<tail用於從前往後遍歷,如果出現有全為空格的,則head最終會等於tail,第二個迴圈就不會執行 while head < tail and s[head] == ' ': head += 1 while head < tail and s[tail - 1] == ' ': tail -= 1 return s[head: tail] # 測試: if __name__ == "__main__": if trim('hello ') != 'hello': print('測試失敗!') elif trim(' hello ') != 'hello': print('測試失敗!') elif trim(' hello world ') != 'hello world': print('測試失敗!') elif trim('') != '': print('測試失敗!') elif trim(' ') != '': print('測試失敗!') else: print('測試成功!')
輸出結果:
遞迴版本:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
def trim(s):
# 如果字串長度為0,或者每輪遞迴完,檢查開頭結尾是否為空格
if len(s) == 0 or s[0] != ' ' and s[-1] != ' ':
return s
# 如果第一個字元是空格,則從第二個字元開始取後面全部,最終遞迴完成時,前面的空格會全部清除完畢
if s[0] == ' ':
return trim(s[1:])
# 如果倒數第一個是空格,則從倒數第二個字元開始往前取全部,最終遞迴完成時,後面的空格會全部清除完畢
if s[-1] == ' ':
return trim(s[:-1])
# 測試:
if __name__ == "__main__":
if trim('hello ') != 'hello':
print('測試失敗!')
elif trim(' hello ') != 'hello':
print('測試失敗!')
elif trim(' hello world ') != 'hello world':
print('測試失敗!')
elif trim('') != '':
print('測試失敗!')
elif trim(' ') != '':
print('測試失敗!')
else:
print('測試成功!')
輸出結果:
3.迭代:即遍歷一個list或tuple,在Python中,是通過for..in來完成的,字串也是可迭代物件,每次迭代的就是字串裡的字元。
如何判斷一個物件是可迭代物件呢?方法是通過collections的Iterable型別判斷:
>>> from collections import Iterable
>>> isinstance('abc', Iterable) # str是否可迭代
True
>>> isinstance([1,2,3], Iterable) # list是否可迭代
True
>>> isinstance(123, Iterable) # 整數是否可迭代
False
如果要對list實現類似於Java的下標迴圈,可以通過Python內建的enumerate函式把一個list裡的元素變成索引-元素對,這樣就可以在for迴圈裡同時迭代索引和元素本身
>>> for i, value in enumerate(['A', 'B', 'C']):
... print(i, value)
...
0 A
1 B
2 C
使用dict的items()可以同時迭代key和value
>>> d = {'x': 'A', 'y': 'B', 'z': 'C' }
>>> for k, v in d.items():
... print(k, '=', v)
...
y = B
x = A
z = C
程式碼如下:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
def findMinAndMax(L):
# 取正負無窮做邊界值
Min = float('inf')
Max = float('-inf')
if len(L) == 0:
return (None, None)
# 迭代list獲取最大值最小值
for i in L:
if i < Min:
Min = i
if i > Max:
Max = i
return (Min, Max)
if __name__ == '__main__':
# 測試
if findMinAndMax([]) != (None, None):
print('測試失敗!')
elif findMinAndMax([7]) != (7, 7):
print('測試失敗!')
elif findMinAndMax([7, 1]) != (1, 7):
print('測試失敗!')
elif findMinAndMax([7, 1, 3, 9, 5]) != (1, 9):
print('測試失敗!')
else:
print('測試成功!')
4.列表生成式,即List Comprehensions,是Python內建的建立list的生成式。
# 生成1-10的list
>>> list(range(1, 11))
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# 按照表達式x*x生成list
>>> [x * x for x in range(1, 11)]
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
# 使用if判斷對生成的數進行篩選
>>> [x * x for x in range(1, 11) if x % 2 == 0]
[4, 16, 36, 64, 100]
# 還可以使用兩層迴圈,生成全排列
>>> [m + n for m in 'ABC' for n in 'XYZ']
['AX', 'AY', 'AZ', 'BX', 'BY', 'BZ', 'CX', 'CY', 'CZ']
# 列出當前目錄下所有檔案和目錄名
>>> import os # 匯入os模組
>>> [d for d in os.listdir('.')] # os.listdir可以列出檔案和目錄
['.emacs.d', '.ssh', '.Trash', 'Adlm', 'Applications', 'Desktop', 'Documents', 'Downloads', 'Library', 'Movies', 'Music', 'Pictures', 'Public', 'VirtualBox VMs', 'Workspace', 'XCode']
# 使用兩個變數來生成list
>>> d = {'x': 'A', 'y': 'B', 'z': 'C' }
>>> [k + '=' + v for k, v in d.items()]
['y=B', 'x=A', 'z=C']
# 把一個list中所有的字串變成小寫
>>> L = ['Hello', 'World', 'IBM', 'Apple']
>>> [s.lower() for s in L]
['hello', 'world', 'ibm', 'apple']
程式碼:
L2=[s.lower() for s in L1 if isinstance(s,str)]
5.生成器:用於按照某種演算法在迴圈中不斷推算出後續的元素,這樣就不必使用列表生成式建立完整的list從而節省大量的空間,這種一邊迴圈一邊計算的機制,稱為生成器generator。
建立generator有兩種方式:
第一種方法很簡單,只要把一個列表生成式的[]改成(),就建立了一個generator
>>> L = [x * x for x in range(10)]
>>> L
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> g = (x * x for x in range(10))
>>> g
<generator object <genexpr> at 0x1022ef630>
列印generator的每一個元素需要使用next(g),用於獲得generator的下一個返回值,沒有更多的元素時,丟擲StopIteration的錯誤。但正確的方法是使用for迴圈列印,因為generator也是可迭代物件。
如果推算的演算法比較複雜,用for迴圈無法實現的時候,就可以用函式來實現
函式實現斐波那契數列
def fib(max):
n, a, b = 0, 0, 1
while n < max:
print(b)
a, b = b, a + b
n = n + 1
return 'done'
如果要將fib函式變成generator,只需要將print(b)改為yield b就可以了
這就是建立generator的另一種方法,如果一個函式定義中包含yield關鍵字,那麼這個函式就不再是一個普通函式,而是一個generator,但是跟函式的執行流程不一樣,函式是順序執行,遇到return語句或者最後一行函式語句就返回,而變成generator的函式,在每次呼叫next()的時候執行,遇到yield語句就返回,再次執行時從上次返回的yield語句處繼續執行,下面的例子很好的說明了這一點
def odd():
print('step 1')
yield 1
print('step 2')
yield(3)
print('step 3')
yield(5)
>>> o = odd()
>>> next(o)
step 1
1
>>> next(o)
step 2
3
>>> next(o)
step 3
5
>>> next(o)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
大佬的程式碼如下:
def triangles():
N = [1]
while len(N) != 11:
yield N
s = N
s.append(0)
N = [s[i - 1] + s[i] for i in range(len(s))]
演算法分析:
每一行的兩端都為1,中間的值為上一行的值從頭兩兩相加所得,上一行元素為n個的話,那麼中間的值有n-1個,因此每一行比上一行多一個數(n-2+1),因此中間的值很好求,即遍歷上一行,使其元素前後兩兩相加,那麼遞推公式是:
[s[i]+s[i+1] for i in range(len(s)-1)]
兩頭再加一個1,楊輝三角就出來了對不對,所以筆者的程式碼是這樣的
def triangles():
N = [1]
while len(N) != 11:
yield N
s = N
N = [s[i] + s[i + 1] for i in range(len(s) - 1)]
N.append(1)
N.insert(0, 1)
那麼大佬的程式碼是怎麼對兩端的1做處理的呢,以N=[1,2,1]為例,s=N,s.append(0)=>[1,2,1,0],len(s)=4,range(4)=[0,1,2,3],用i去遍歷的時候,當i=0,s[i-1]+s[i]=0+1=1,當i=1,s[0]+s[1]=3,當i=2,s[1]+s[2]=3,當i=3,s[2]+s[3]=1,最後得出[1,3,3,1],此處對這個加0操作給予膜拜ORZ
6.迭代器:可以直接作用於for迴圈的資料型別有以下幾種,一類是集合資料型別,如list、tuple、dict、set、str等,一類是generator,包括生成器和帶yield的函式。前面有提到生成器是可以被next()呼叫並返回下一個值的,那麼,可以被next()函式呼叫並不斷返回下一個值的物件稱為迭代器Iterator,生成器都是Iterator物件,但list、dict、str雖然是Iterable,但不是Iterator,如果要把Iterable變成Iterator,可以使用iter()函式
Python的Iterator物件表示的是一個數據流,被next()函式呼叫時候會不斷返回下一個資料,因此可以表示一個無限大的資料流,而且Iterator的計算是惰性的,只有在需要返回下一個資料時它才會計算。