1. 程式人生 > >Python學習筆記:Python高階特性

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的計算是惰性的,只有在需要返回下一個資料時它才會計算。