廖雪峯Python教程學習筆記(3)

6. 高級特性

Python 的高級特性有助於我們寫出簡單、少量的代碼。

6.1 切片

在 Python 中,listtuplestr 都可以使用切片操作。通過切片操作,可以取出部分元素,例如取出指定索引區域的元素,按一定間隔取出元素。特別地,對於 str 而言,切片操作就起到了截取的作用。

# 取 list 或者 tuple 的部分元素
L = ['Michael', 'Sarah', 'Tracy', 'Bob', 'Jack']
print(L)
# 取出前 3 個元素
print([L[0], L[1], L[2]]) # ['Michael', 'Sarah', 'Tracy']

# 取出前 N 個元素
n = 4
r = []
for i in range(n):
    r.append(L[i])
print(r)

# 使用 slice 操作符

# L[0:3] 表示從 L 的索引 0 開始取,到索引 3 結束,但是不包括索引 3,也就是說取索引 0,1,2 這 3 個元素。
print(L[0:3]) # ['Michael', 'Sarah', 'Tracy']
# 如果是從索引 0 開始,可以省略開始索引
print(L[:3]) # ['Michael', 'Sarah', 'Tracy']
# 如果是取到尾部,可以省略尾部索引
print(L[1:]) # ['Sarah', 'Tracy', 'Bob', 'Jack']
# 支持倒數切片
# 從倒數第 2 個開始,取完。
print(L[-2:]) # ['Bob', 'Jack']
# 從倒數第 2 個開始,取到倒數第 1 個
print(L[-2:-1]) # ['Bob']

print(L[3:0]) # []
print(L[-1:-2]) #[]

# 切片操作進一步學習
L = list(range(100))
print(L)
# 取出前 10 個數
print(L[:10]) # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
# 取出後 10 個數
print(L[-10:]) # [90, 91, 92, 93, 94, 95, 96, 97, 98, 99]
# 前 11 到 20 個數
print(L[11:20]) # [11, 12, 13, 14, 15, 16, 17, 18, 19]
# 前 10 個數,每兩個取一個
print(L[:10:2]) # [0, 2, 4, 6, 8]
# 所有數,每五個取一個
print(L[::5]) # [0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95]
# 複製一個 list
print(L[:])

# tuple 使用切片操作後,結果仍是 tuple
print((0, 1, 2, 3, 4, 5)[:3]) # (0, 1, 2)

# 字符串使用切片操作,結果仍是字符串
print('ABCDEF'[:3]) # ABC
print('ABCDEF'[::2]) # ACE

需要注意的是,切片操作都是從小索引,到大索引的;反過來,卻不行。

6.2 迭代

在 Python 中,任何可迭代對象都可以作用於 for 循環,包括 listtupledictstr ,還包括自定義的可迭代對象。

# 迭代 list
L = [1, 3, 5, 7, 9]
for num in L:
    print(num)

# 迭代 tuple
T = ('a', 'b', 'c', 'd')
for ch in T:
    print(ch)

# 迭代 dict
D = {'name' : 'wangzhichao', 'age' : 18}
# 迭代 dict 中的 key, 這也是默認情況
for key in D:
    print(key)
# 迭代 dict 中的 value
for value in D.values():
    print(value)
# 同時迭代 dict 中的 key 和 value
for k, v in D.items():
    print(k, '->', v)

# 迭代字符串
for ch in 'ABC':
    print(ch)

# 判斷一個對象是否是可迭代對象
from collections import Iterable

print(isinstance('abcde', Iterable)) # True
print(isinstance([1, 2, 3], Iterable)) # True
print(isinstance(1234, Iterable)) # False

# 對 list 實現下標迭代
for index, value in enumerate(['A', 'B', 'C']):
    print(index, value)

enumerate() 函數:這是一個內置函數,可以把一個 可迭代對象變成索引-元素對。Return an enumerate object. iterable must be a sequence, an iterator, or some other object which supports iteration。
Iterable 表示一個可迭代對象。

6.3 列表生成式

列表生成式,即 List Comprehensions,是 Python 內置的簡單卻強大的用來創建 list 的生成式。

# 生成 [1x1, 2x2, 3x3, ..., 10x10]
print([x * x for x in range(1, 11)]) # [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

看一下上面例子中列表生成式的寫法:x * x 是要生成的元素,放在最前面;然後,接着是一個 for 循環,這樣就把需要的 list 創建了出來。

下邊是列表生成式的演示代碼:

# 生成 1 到 10 偶數的平方的 list
print([x * x for x in range(1, 11) if x % 2 == 0]) # [4, 16, 36, 64, 100]

# 使用兩層循環,生成全排列
print([m + n for m in 'ABC' for n in 'XYZ']) # ['AX', 'AY', 'AZ', 'BX', 'BY', 'BZ', 'CX', 'CY', 'CZ']

# 列出當前目錄下的所有文件和目錄名
import os # 導入 os 模塊
print([d for d in os.listdir('.')]) # ['do_iter.py', 'do_iter_test.py', 'do_listcompr.py', 'do_slice.py', 'do_slice_test.py']

# 使用兩個變量來生成list
print([k + '=' + v for k, v in {'x' : 'A', 'y' : 'B', 'z' : 'C'}.items()]) # ['x=A', 'y=B', 'z=C']

# 把一個list中所有的字符串變成小寫
L = ['Hello', 'World', 'Adups', 'Abup']
print([s.lower() for s in L]) # ['hello', 'world', 'adups', 'abup']

需要注意的是,列表生成式中的 forif 不再使用 :,還有 continue 不能用在 for語句中了。

6.4 生成器

通過列表生成式,可以直接創建一個列表。但是,如果列表生成式一次生成的數據量太大,比如 100 萬條,就會佔據不小的內存。
如果列表元素可以按照某種算法推算出來,我們就可以在循環的過程中不斷推算出後續的元素了。這樣就不必一次創建很大的集合,從而節省內存空間。在 Python 中,就存在這種一邊循環一邊計算的機制,這就是生成器:generator。
創建一個 generator,有兩種方法:
第一種方法:只要把一個列表生成式的 [] 改成 (),就創建了一個 generator。

# 創建一個 generator
L = [x * x for x in range(10)]
print(L) # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
g = (x * x for x in range(10))
print(g) # <generator object <genexpr> at 0x00000000024E3510>

取出 generator 中的元素,可以通過 next() 函數。這裏需要說明一下 next() 函數是一個內置函數。

next(iterator[, default])
Retrieve the next item from the iterator by calling its __next__() method. If default is given, it is returned if the iterator is exhausted, otherwise StopIteration is raised.
從迭代器裏獲取下一個元素。如果 default 參數被指定了,那麼當迭代器迭代完時就返回指定的 default 值,否則就會拋出 StopIteration 異常。

print(next(g)) # 0
print(next(g)) # 1
print(next(g)) # 4
print(next(g)) # 9 
print(next(g)) # 16
print(next(g)) # 25
print(next(g)) # 36
print(next(g)) # 49
print(next(g)) # 64
print(next(g)) # 81
# print(next(g)) # 報錯
'''
Traceback (most recent call last):
  File "do_generator.py", line 17, in <module>
    print(next(g))
StopIteration
'''

使用 next() 函數取出 generator 中的元素並不優雅,好的方式是使用 for 循環,因爲 generator 也是一個 Iterable 對象。

g = (x * x for x in range(10))
print(isinstance(g, Iterable)) # True
for n in g:
    print(n)
'''
打印結果:
True
0
1
4
9
16
25
36
49
64
81
'''

第二種方法:使用函數來實現,這種方法適用於推算的算法比較複雜,用類似列表生成式的 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(6)

上面代碼中的 a, b = b, a + b 等價於

t = (b, a + b) # t是一個tuple
a = t[0]
b = t[1]

毋庸置疑地,a, b = b, a + b 這種寫法更加簡潔,因爲我們可以不寫臨時變量 t

好了,那麼怎樣把上面的函數 fib() 變成一個 generator 呢?
只要把 fib() 函數中的 print(b) 改成 yield b ,就 ok 了。

def fib2(max):
    n, a, b = 0, 0, 1
    while n < max:
        yield b
        a, b = b, a + b
        n = n + 1
    return 'done'

調用一下:

f = fib2(6)
print(f) # <generator object fib2 at 0x00000000026682E0>
for x in f:
    print(x)
'''
打印:
1
1
2
3
5
8
'''

這裏涉及了一個新的關鍵字:yield。需要好好理解一下這個概念。
我們知道函數是順序執行的,遇到 return 語句或執行到函數最後一行語句時,函數就會返回。但是,generator 卻有些區別:generator 在每次調用 next() 函數的時候,遇到 yield 語句就返回;下次再調用 next() 函數的時候,從上次返回的 yield 語句處繼續執行。

獲取 generator 的返回值:

# 獲取 generator 的返回值
g = fib2(6)
while True:
    try:
        x = next(g)
        print('g:', x)
    except StopIteration as e:
        print('Generator return value:', e.value)
        break
'''
打印結果:
g: 1
g: 1
g: 2
g: 3
g: 5
g: 8
Generator return value: done
'''

6.5 迭代器

看到迭代器,可能會想到之前學習的 Iterable,但是不能把兩者混爲一談:

Iterable 是可迭代對象,指可以直接作用於 for 循環的對象。
Iterator 纔是迭代器,指可以被 next() 函數調用並不斷返回下一個值的對象。

那麼,有哪些對象是 Iterable 對象,又有哪些對象是 Iterator 對象呢?
Iterable 對象主要包括兩類:

  • 一類是集合類數據類型,如 listtupledictsetstrbytesrange 等;
  • 一類是生成器,包括類似列表生成式的生成器和帶 yield 的生成器函數。
# 判斷對象是否是 Iterable 對象
from collections.abc import Iterable, Iterator
print(isinstance([], Iterable)) # True, 說明 list 是 Iterable 對象
print(isinstance((), Iterable)) # True,說明 tuple 是 Iterable 對象
print(isinstance({}, Iterable)) # True,說明 dict 是 Iterable 對象
print(isinstance({1, 2, 3}, Iterable)) # True,說明 set 是 Iterable 對象
print(isinstance('hello', Iterable)) # True,說明 str 是 Iterable 對象
print(isinstance(b'hello', Iterable)) # True, 說明 bytes 是 Iterable 對象
print(isinstance(range(10), Iterable)) # True, 說明 range 是 Iterable 對象

g = (x * x for x in range(10))
print(isinstance(g, Iterable)) # True,說明生成器是 Iterable 對象
def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        yield b
        a, b = b, a + b
        n = n + 1
    return 'done'
f = fib(6)
print(isinstance(f, Iterable)) # True,說明生成器是 Iterable 對象

生成器都是 Iterator 對象,但 listdictstr 等雖是 Iterable,卻不是 Iterator。通過使用 iter() 函數可以把 listdictstrIterable 轉成 Iterator

# 判斷對象是否是 Iterator 對象
print(isinstance([], Iterator)) # False, 說明 list 不是 Iterator 對象
print(isinstance((), Iterator)) # False,說明 tuple 不是 Iterator 對象
print(isinstance({}, Iterator)) # False,說明 dict 不是 Iterator 對象
print(isinstance({1, 2, 3}, Iterator)) # False,說明 set 不是 Iterator 對象
print(isinstance('hello', Iterator)) # False,說明 str 不是 Iterator 對象
print(isinstance(b'hello', Iterator)) # False, 說明 bytes 不是 Iterator 對象
print(isinstance(range(10), Iterator)) # False, 說明 range 不是 Iterator 對象

g = (x * x for x in range(10))
print(isinstance(g, Iterator)) # True,說明生成器是 Iterator 對象
def fib2(max):
    n, a, b = 0, 0, 1
    while n < max:
        yield b
        a, b = b, a + b
        n = n + 1
    return 'done'
f = fib2(6)
print(isinstance(f, Iterator)) # True,說明生成器是 Iterator 對象

print('*' * 20)

# 把集合類數據類型轉爲 Iterator
print(isinstance(iter([]), Iterator)) # True
print(isinstance(iter(()), Iterator)) # True
print(isinstance(iter({}), Iterator)) # True
print(isinstance(iter({1, 2, 3}), Iterator)) # True
print(isinstance(iter('hello'), Iterator)) # True
print(isinstance(iter(b'hello'), Iterator)) # True
print(isinstance(iter(range(10)), Iterator)) # True

需要說明一下,iter() 函數是 Python 的內置函數,它的作用是返回一個 Iterator 對象。

發佈了76 篇原創文章 · 獲贊 44 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章