4.9 可迭代物件
Python中有6種基本的資料型別,除了數字型別,其它5種型別都是可迭代物件,掌握可迭代物件的特性和方法是非常有必要的,
4.9.1 可迭代(iterable)、可迭代物件
4.9.1.1 相關概念
迭代(遍歷)就是按照某種順序逐個訪問物件中的每一項,
Python中有很多物件都是可以通過for陳述句來直接遍歷的,例如list、string、dict等等,這些物件都是可迭代的,被稱為可迭代物件,
可以將可迭代物件想象成一個容器,里面存放了有限個元素,并且每個元素都可以從中獲取出來,那么這個容器就是可迭代的,這個容器就是可迭代物件,
所有的可迭代物件都需要實作__iter__方法,該方法就是用于當我們在回圈時將可迭代物件轉換成迭代器的,
4.9.1.2 可迭代物件的判斷
- 可以通過hasattr(obj, 'iter')來判斷obj是否為可迭代物件,
- 可以通過'iter' in dir(obj)來判斷obj是否為可迭代物件,
- 也可以通過isinstance來進行判斷:
from typing import *
dct = {'one': 1, 'two': 2, 'three': 3, 'zero': 0}
print(isinstance(dct, Iterable))
True
4.9.1.3 可迭代物件的特點
- 私有方法多,操作靈活(比如串列,字典的增刪改查,字串的常用操作方法等)
- 直觀,可以直接看到里面的資料
- 占用記憶體
- 不能直接通過回圈迭代取值(需要先通過__iter__方法將其轉換成迭代器)
當你側重于對于資料可以靈活處理,并且記憶體空間足夠,將資料集設定為可迭代物件是明確的選擇,
4.9.2 迭代器(iterator)
4.9.2.1 相關概念
迭代器是可迭代物件的一個子集,是一個可以記住遍歷的位置的物件,它與串列、元組、集合、字串這些可迭代物件的區別就在于__next__()方法的實作,也就是通過該方法可以一個個的將元素取出來,
迭代器支持__iter__()和__next__()方法,其中:
__iter__()方法回傳迭代器物件本身,而可迭代物件的該方法則回傳其迭代器,
__next__()方法回傳容器的下一個元素,在結尾時引發StopIteration例外,
4.9.2.1.1 迭代的本質
遍歷的本質其實就是在我們使用for或者while回圈可迭代物件時會自動呼叫該可迭代物件的__iter__()方法得到其迭代器,然后通過這個迭代器的__next__()方法一步步獲取下一個元素,直到最后一個元素引發StopIteration例外,
對于迭代器,其__next__()方法和next(迭代器)等效,都是用來獲取下一個元素,
4.9.2.2 迭代器的創建
4.9.2.2.1 通過iter()函式創建迭代器
通過iter函式可以很簡單的將可迭代物件轉換成迭代器,
示例1:
>>> from typing import *
>>> iterable = [1, 2, 4]
>>> it = iter(iterable)
>>> print(isinstance(it, Iterable)) # 是否可迭代物件
>>> print(isinstance(it, Iterator)) # 是否迭代器
True
True
示例2:
s = 'ABC' # s此時是一個可迭代物件
it = iter(s) # 轉換成迭代器,如果此時報錯,則表示s不是一個可迭代物件,
for i in range(len(s)):
print(s[i]) # 遍歷s中的所有元素
print(it.__next__)
print(it.__next__()) # 迭代獲取元素
print(next(it)) # 繼續迭代獲取元素
A
B
C
<method-wrapper 'next' of str_iterator object at 0x0000013D53672D40>
A
B
4.9.2.2.2 通過filter函式創建
參見可迭代物件相關函式的filter函式
4.9.2.2.3 通過map函式創建
參見可迭代物件相關函式的map函式
4.9.2.2.4 通過itertools創建
除了Python內置的iter之外,還可以通過Python內置的工具包itertools創建迭代器,其中函式包括:
count
cycle
repeat
accumulate
chain
compress
dropwhile
islice
product
permutations
combinations
......
itertools中包含很多用于創建迭代器的實用方法,如果感興趣可以訪問官方檔案進行詳細了解,
4.9.2.2.5 將可迭代物件改造成迭代器物件
class iterator_list(object):
def __init__(self,a):
self.a=a
self.len=len(self.a)
self.cur_pos=-1
def __iter__(self):
return self
def __next__(self):
self.cur_pos +=1
if self.cur_pos<self.len:
return self.a[self.cur_pos]
else:
raise StopIteration() # 表示至此停止迭代
4.9.2.3 迭代器的判斷
- 可以通過hasattr(obj, 'next')來判斷obj是否為迭代器,
- 可以通過'next' in dir(obj)來判斷obj是否為迭代器,
- 可以通過isinstance(obj, Iterator)來進行判斷是否為迭代器(需要匯入typing模塊),
4.9.2.4 迭代器的特點
- 可以直接通過回圈迭代取值
- 資料不直觀,操作方法單一
- 迭代器是一種支持next()操作的物件,它包含了一組元素,當執行next()操作時,回傳其中一個元素,當所有元素都被回傳后,再執行next()報例外:StopIteration
- 迭代器是訪問集合元素的一種方式
- 迭代器物件表示的是一個資料流,可以把它看作一個有序序列,但我們不能提前知道序列的長度,只有通過nex()函式實作需要計算的下一個資料,
- 成員資格檢查時,會從迭代器中按順序獲取元素并判斷,當能有判斷結果時,會記錄當前的位置,
r = iter(range(10))
print(5 in r)
for i in r:
print(i)
True
6
7
8
9
r = iter(range(10))
print(5 in r)
print(5 in r)
True
False
r = iter(range(10))
print(11 in r)
for i in r:
print(i)
False
你的資料量過大,大到足以撐爆你的記憶體或者你以節省記憶體為首選因素時,將資料集設定為迭代器是一個不錯的選擇,節省記憶體,按需取值,
4.9.3 生成器(generator)
4.9.3.1 相關概念
生成器是迭代器的子集,生成器一定是迭代器,但是迭代器不全是生成器,在Python中一個函式可以用yiled或yiled from替代return回傳值,這樣的話這個函式就變成了一個生成器物件,
4.9.3.2 生成器的判斷
可以通過isinstance(obj, Generator)來進行判斷是否為迭代器(需要匯入typing模塊),
4.9.3.3 生成器的創建
4.9.3.3.1 通過函式創建(生成器函式)
通過yield
def fib(number):
n, a, b = 0, 0, 1
while n < number:
yield b
a, b = b, a + b
n += 1
gen = fib(5)
print(gen)
print(type(gen))
<generator object generator at 0x0000025EAEEF1EE0>
<class 'generator'>
def func():
print('111')
yield 222
print('123')
yield 1211
gener = func()
print(gener.__next__())
print(gener.__next__())
print(gener.__next__()) # 會報錯
111
222
123
1211
Traceback (most recent call last):
File "C:\Users\furongbing\PycharmProjects\pythonProject\t2.py", line 11, in
print(gener.next()) # 會報錯
StopIteration
通過yield from
def generator(array): # 其中:array為可迭代物件
for sub_array in array:
yield from sub_array
gen = generator([1, 2, 3])
print(gen)
print(type(gen))
<generator object generator at 0x00000251434483C0>
<class 'generator'>
yield from相對于yield的有幾個主要優點:
- 代碼更加簡潔
- 可以用于生成器嵌套(yield直接回傳后面的內容,yield from可以將后面跟著的迭代物件當成生成器回傳)
- 易于例外處理
yield 與 return 的區別: - return一般在函式中只設定一個,他的作用是終止函式,并且給函式的執行者回傳值,
- yield在生成器函式中可設定多個,他并不會終止函式,next會獲取對應yield生成的元素,
4.9.3.3.2 通過推導式創建(生成器運算式)
>>> gen = (x*2 for x in range(10))
>>> gen
>>> type(gen)
<generator object generator at 0x00000251434483C0>
<class 'generator'>
串列推導式和生成器推導式的區別:
- 串列推導式比較耗記憶體,所有資料一次性加載到記憶體;而生成器運算式遵循迭代器協議,逐個產生元素,
- 串列推導式一目了然,生成器運算式只是一個記憶體地址,
生成器的優點: - 代碼簡潔
- 運行速度快
- 節省記憶體
4.9.3.4 生成器的特點
- 生成器本質上就是一個函式,它記住了上一次回傳時在函式體中的位置,目的就是用來生成元素的,
- 對生成器函式的第二次(或第n次)呼叫,跳轉到函式上一次掛起的位置,而且記錄了程式執行的背景關系,
- 生成器不僅記住了它的資料狀態,生成器還記住了程式執行的位置,
- 生成器一定是可迭代的,也一定是迭代器物件,
4.9.4 可迭代物件的通用操作
4.9.4.1 可迭代物件的遍歷
可迭代物件都可以進行遍歷的操作,就是將其中的元素一個個取出來進行操作,
Iter = 'Python'
for i in Iter:
print(i)
P
y
t
h
o
n
對于序列,不但能遍歷里面所有的元素,還能遍歷元素的索引,關于序列,將在序列中詳細介紹,
用range()和len()函式遍歷得到索引和值
seq = 'Python'
for i in range(len(seq)):
print(f'索引:{i}, 元素:{seq[i]}')
索引:0, 元素:P
索引:1, 元素:y
索引:2, 元素:t
索引:3, 元素:h
索引:4, 元素:o
索引:5, 元素:n
通過enumerate函式遍歷得到索引和值
seq = 'Python'
for i, v in enumerate(seq):
print(f'索引:{i}, 元素:{v}')
索引:0, 元素:P
索引:1, 元素:y
索引:2, 元素:t
索引:3, 元素:h
索引:4, 元素:o
索引:5, 元素:n
使用zip函式同時遍歷多個序列
seq1 = 'Python'
seq2 = 'Java'
for q, a in zip(seq1, seq2):
print(q, a)
P J
y a
t v
h a
注意:上述用字串做演示,實際上其它序列物件也是一樣的
4.9.4.2 成員資格檢查
可以用in和not in檢查某個元素是否在目標可迭代物件中,可迭代物件都支持成員資格檢查操作,
>>> Iter = 'Python'
>>> 'P' in Iter
>>> 'a' not in Iter
True
True
注意:上述用字串做演示,實際上其它可迭代物件也是一樣的
4.9.4.3 解包(拆包)、裝包
4.9.4.3.1 概念
解包的英文是unpacking,就是將容器里的元素全部取出來,在Python中,所有的可迭代物件都支持解包,有些情況中,解包會自動完成,在另外一些情況中,可以手動進行解包,
裝包和解包相反,就是將一些元素打包放到一起,
4.9.4.3.2 自動解包
在進行變數賦值的時候,如果變數的數量和右邊可迭代物件中元素的數量相同時,解包會自動完成,
a, b = [1, 2]
print(a, b)
c, d = {'one': 1, 'two': 2}
print(c, d)
1 2
one two
我們在進行變數賦值的時候,經常會用到下面這種方式,同時為不同的變數賦值不同的資料:
>>> e, f = 5, 6
實際上這就是利用到了解包,后面的5, 6其實就是可迭代物件(元組),然后進行自動解包后賦值給了變數e和f,
這里值得注意的是,字典在進行自動解包時獲得的是字典的key,
4.9.4.3.3 手動解包
可以通過在可迭代物件前面添加*符號進行手動解包,解包后的結果是可迭代物件中所包含的所有的元素,
因為是多個元素,所以不能對解包后的結果使用len等只支持單個引數的函式,否則會報錯,但是可以使用max等可以接收多個引數的函式,
手動解包一般是用在函式接收引數中,
lst = [1, 2, 4]
print(*lst)
print(max(*lst))
print(len(*lst))
1 2 4
4
Traceback (most recent call last):
File "E:\studypy\tmp5.py", line 4, in
print(len(*lst))
TypeError: len() takes exactly one argument (3 given)
4.9.4.3.4 裝包
在賦值或者函式傳參時,如果在等式左邊變數名添加*則可以進行裝包,
lst = [1, 2, 4, 8, 16]
dct = {'one': 1, 'two': 2, 'three': 3, 'four': 4}
a, b, *c = lst
print(a, b, c)
d, *e, f = dct
print(d, e, f)
1 2 [4, 8, 16]
one ['two', 'three'] four
dct = {'one': 1, 'two': 2, 'three': 3, 'four': 4}
a, *b, c = dct.items()
print(a, b, c)
('one', 1) [('two', 2), ('three', 3)] ('four', 4)
用*進行裝包后的變數型別其實就是串列,在函式中則是元組,函式的解包與裝包將會在函式章節中詳細介紹,
4.9.5 可迭代物件相關函式
4.9.5.1 next(Iterator[,default])
描述
通過呼叫迭代器的__next__()方法從迭代器中檢索下一項,如果給出了默認值,則在迭代器耗盡時回傳默認值,否則將引發 StopIteration,
示例
a = iter([1, 2, 3])
print(next(a))
print(next(a))
print(next(a))
print(next(a, '無資料了'))
1
2
3
無資料了
4.9.5.2 all(iterable)、any(iterable)
描述
對于all函式,如果 iterable 的所有元素均為真值或可迭代物件為空則回傳 True,否則回傳False,any函式則是只要有一個元素為真值,則回傳True,否則回傳False,若iterable為空,也回傳False,
示例
a = []
b = [1, 2]
c = [1, 0]
d = [0, 0]
print(all(a))
print(all(b))
print(all(c))
print(all(d))
print(any(a))
print(any(b))
print(any(c))
print(any(d))
True
True
False
False
False
True
True
False
4.9.5.3 enumerate(iterable, start=0)
描述
回傳一個列舉物件,iterable 必須是一個序列,或 iterator,或其他支持迭代的物件, enumerate() 回傳的迭代器的 next() 方法回傳一個元組,里面包含一個計數值(從 start 開始,默認為 0)和通過迭代 iterable 獲得的值,
示例
dct = {'one': 1, 'two': 2}
for i, j in enumerate(dct):
print(i, j)
0 one
1 two
4.9.5.4 filter(function, iterable)
描述
將iterable中的元素作為function函式的輸入,然后將回傳結果為真的元素構建一個新的迭代器,
如果function是None,則會假設它是一個身份函式,即iterable中所有回傳假的元素會被移除,
請注意,filter(function, iterable)相當于一個生成器運算式,當 function 不是None時等效為:(item for item in iterable if function(item))
function是None時等效為:(item for item in iterable if item)
示例
from typing import *
a = [1, 2, 3, 4]
r = filter(lambda x: x - 2, a)
print(list(r))
print(type(r))
print(isinstance(r, Iterable))
print(isinstance(r, Iterator))
print(isinstance(r, Generator))
[1, 3, 4]
<class 'filter'>
True
True
False
4.9.5.5 len(iterable)
描述
回傳可迭代物件中包含的元素的個數,
示例
>>> len('string')
>>> len([1, 2, 3])
6
3
4.9.5.6 map(function, iterable, ...)
描述
回傳一個將 function 應用于 iterable 中每一項并輸出其結果的迭代器, 如果傳入了額外的 iterable 引數,function 必須接受相同個數的實參并被應用于從所有可迭代物件中并行獲取的項, 當有多個可迭代物件時,最短的可迭代物件耗盡則整個迭代就將結束,
示例
from typing import *
a = [1, 2, 3, 4]
b = [2, 4, 6, 8]
r = map(lambda x, y: x + y, a, b)
print(list(r))
print(type(r))
print(isinstance(r, Iterable))
print(isinstance(r, Iterator))
print(isinstance(r, Generator))
[3, 6, 9, 12]
<class 'map'>
True
True
False
4.9.5.7 max(str)、min(str)
描述
回傳字串中編號最大(最小)的字符,
示例
s = 'pythonTian賽車'
print(min(s))
print(max(s))
T
車
4.9.5.8 sum(iterable, start=0)
描述
從 start 開始自左向右對 iterable 的項求和并回傳總計值, iterable 的項通常為數字,而 start 值則不允許為字串,
實體
print(sum([1, 2, 3, 4]))
print(sum([1, 2, 3, 4], 3))
10
13
4.9.5.9 zip(*iterables, strict=False)
描述
在多個迭代器上并行迭代,從每個迭代器回傳一個資料項組成元組,strict引數為True用來檢查所有的可迭代物件的長度是否一致,如果不一致則觸發 ValueError,為False時則不檢查,
示例
for item in zip([1, 2, 3], ['sugar', 'spice', 'everything nice']):
print(item)
(1, 'sugar')
(2, 'spice')
(3, 'everything nice')
4.9.6 序列(seq)
序列一定是可迭代物件,而可迭代物件不一定是序列,對于可迭代物件,如果元素是有序的,則是序列,
序列是一種特殊的可迭代物件,序列中的元素不僅可迭代,而且有順序,可以通過索引或切片的方式獲取序列中的元素,
Python中有6中基本的資料型別,其中字串、串列、元組都是序列,
4.9.6.1 序列的拼接和重復
序列可以進行拼接和重復以進行擴展,
使用'+'進行拼接
通過+可以將兩種相同型別的序列進行拼接,并且拼接之后的資料型別是原來的型別,
>>> 'pyth' + 'on'
'python'
相鄰的2個字串會自動合并:
>>> 'pyt' 'hon'
'python'
>>> [1, 2, 3] + [4, 5, 6]
[1, 2, 3, 4, 5, 6]
>>> (1, 2, 3) + (4, 5, 6)
(1, 2, 3, 4, 5, 6)
使用'*'進行重復
通過*可以將兩種相同型別的序列進行重復,并且重復之后的資料型別是原來的型別,
>>> 'egg' * 3
'eggeggegg'
>>> [1, 2] * 2
[1, 2, 1, 2]
>>> (1, 2) * 2
(1, 2, 1, 2)
4.9.6.2 序列索引
序列支持索引(下標訪問),第一個字符的索引是 0,之后每個元素的索引依次增加1,
>>> seq = 'Python'
>>> seq[0]
>>> seq[5]
'P'
'n'
索引還支持負數,用負數索引時,從右邊開始計數:
>>> seq = 'Python'
>>> seq[-1]
>>> seq[-2]
>>> seq[-6]
'n'
'o'
'P'
注意,-0 和 0 一樣,因此,負數索引從 -1 開始,
索引位置參考下圖:
+---+---+---+---+---+---+
| P | y | t | h | o | n |
+---+---+---+---+---+---+
0 1 2 3 4 5
-6 -5 -4 -3 -2 -1
索引越界會報錯:
>>> seq = 'Python'
>>> seq[42]
Traceback (most recent call last):
File "", line 1, in
IndexError: string index out of range
注意:上述用字串做演示,實際上串列和元組也是一樣的
4.9.6.3 序列切片
除了索引,序列還支持切片,索引可以提取單個元素,切片則提取多個元素,參考下圖:
+---+---+---+---+---+---+
| P | y | t | h | o | n |
+---+---+---+---+---+---+
0 1 2 3 4 5 6
-6 -5 -4 -3 -2 -1
注意:上面的數字對應的是每個字符中間的分隔'|'線,
序列的切片語法格式如下:
序列[頭下標:尾下標]
>>> seq = 'Python'
>>> seq[0:2]
>>> seq[2:5]
'Py'
'tho'
切片索引的默認值很有用;省略開始索引時,默認值為 0,省略結束索引時,默認為到字串的結尾:
>>> seq = 'Python'
>>> seq[:2]
>>> seq[4:]
>>> seq[-2:]
'Py'
'on'
'on'
注意,輸出結果包含切片開始,但不包含切片結束,因此,seq[:i] + seq[i:] 總是等于 seq:
>>> seq = 'Python'
>>> seq[:2] + string[2:]
>>> seq[:4] + string[4:]
'Python'
'Python'
對于使用非負索引的切片,如果兩個索引都不越界,切片長度就是起止索引之差,例如, seq[1:3] 的長度是 2,
切片會自動處理越界索引:
>>> seq = 'Python'
>>> seq[4:42]
>>> seq[42:]
'on'
''
指定步長的切片
>>> seq = 'Python'
>>> seq[0:6:2] # 從第0個元素到第5個元素,每隔2個就提取一個出來
'Pto'
>>> seq[:] # 淺拷貝,結果同string
'Python'
>>> seq[::-1] # 翻轉整個字串
'nohtyP'
步長為負數時,先翻轉然后再每隔步長個字符就提取一個出來,
注意:
1、上述用字串做演示,實際上串列和元組也是一樣的
2、字串切片后回傳的是字串,同理,串列、元組切片后回傳的也是串列、元組,
4.9.6.4 序列方法
4.9.6.4.1 count(x)
描述
回傳str在seq里面出現的次數,未找到則回傳0
示例
seq = ' Python tian \t mao \n taobao '
print(seq.count('o'))
print(seq.count('ao'))
print(seq.count('io'))
4
1
0
4.9.6.4.2 index(x)、rindex(x)
描述
回傳str在seq里面出現的索引,未找到則報錯,rindex表示從右邊開始查找,
示例
seq = ' Python tian \t mao \n taobao '
print(seq.index('o'))
print(seq.index('ao'))
print(seq.index('io'))
5
16
Traceback (most recent call last):
File "E:\studypy\tmp.py", line 4, in
print(s.index('io'))
ValueError: substring not found
4.9.6.5 序列函式
4.9.6.5.1 reversed(seq)
描述
回傳序列的反向序列的迭代器,經過測驗,對可迭代物件也生效,
示例
lst = [1, 2, 4]
re = reversed(lst)
print(re)
print(lst)
print(list(re))
<list_reverseiterator object at 0x0000021BE7523FA0>
[1, 2, 4]
[4, 2, 1]
4.9.6.5.2 sorted(iterable, /, *, key=None, reverse=False)
描述
根據 iterable 中的項回傳一個新的已排序串列,具有兩個可選引數,它們都必須指定為關鍵字引數,
key 指定帶有單個引數的函式,用于從 iterable 的每個元素中提取用于比較的鍵 (例如 key=str.lower), 默認值為 None (直接比較元素),
reverse 為一個布林值, 如果設為 True,則每個串列元素將按反向順序比較進行排序,
示例1:簡單排序
>>> sorted([5, 2, 3, 1, 4])
[1, 2, 3, 4, 5]
示例2:對元素呼叫函式排序
>>> sorted("This is a test string from Andrew".split(), key=str.lower)
['a', 'Andrew', 'from', 'is', 'string', 'test', 'This']
示例3:將元素的某個索引作為key進行排序
student_tuples = [
('john', 'A', 15),
('jane', 'B', 12),
('dave', 'B', 10),
]
r = sorted(student_tuples, key=lambda x: x[2], reverse=True)
print(r)
[('john', 'A', 15), ('jane', 'B', 12), ('dave', 'B', 10)]
多個關鍵字的排序
student_tuples = [
('john', 'A', 15),
('john', 'A', 20),
('jane', 'B', 12),
('dave', 'B', 10),
('dave', 'B', 5),
]
r = sorted(student_tuples, key=lambda x: (x[0], x[2]))
print(r)
[('dave', 'B', 5), ('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15), ('john', 'A', 20)]
更多排序的內容可以參照:
Operator 模塊排序
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/508920.html
標籤:Python
