日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > python >内容正文

python

python死磕八之迭代器与生成器

發布時間:2023/12/10 python 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 python死磕八之迭代器与生成器 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

  迭代是Python最強大的功能之一。初看起來,你可能會簡單的認為迭代只不過是處理序列中元素的一種方法。 然而,絕非僅僅就是如此,還有很多你可能不知道的, 比如創建你自己的迭代器對象,在itertools模塊中使用有用的迭代模式,構造生成器函數等等。

  一、for循環的內部原理?

  for循環迭代遍歷對象首先是給對象添加了一個__iter__方法,使之每次可以調用next去訪問下一個元素,直至遍歷完遇到了StopIteration異常:

>>> items = [1, 2, 3] >>> # Get the iterator >>> it = iter(items) # Invokes items.__iter__() >>> # Run the iterator >>> next(it) # Invokes it.__next__() 1 >>> next(it) 2 >>> next(it) 3 >>> next(it) Traceback (most recent call last):File "<stdin>", line 1, in <module> StopIteration >>>

?

  二、你想反方向迭代一個序列

  之前思路:調用reverse方法,再for循環遍歷

  遺漏點:當我們需要處理的對象有__reverse__方法才行,比如我們給定一個Countdown類,想讓他實現reverse方法:

class Countdown():def __init__(self,start):self.start = startdef __iter__(self):n = self.startwhile n > 0:yield nn -= 1def __reversed__(self):n = 0while n <= self.start:yield nn += 1

  當定義了__reversed__方法后,我們可以調用reverse方法了:

for i in Countdown(30):print(i)for i in reversed(Countdown(30)):print(i)

?

  三、你想得到一個由迭代器生成的切片對象,但是標準切片操作并不能做到。

  函數?itertools.islice()?正好適用于在迭代器和生成器上做切片操作。比如:

>>> def count(n): ... while True: ... yield n ... n += 1 ... >>> c = count(0) >>> c[10:20] Traceback (most recent call last):File "<stdin>", line 1, in <module> TypeError: 'generator' object is not subscriptable>>> # Now using islice() >>> import itertools >>> for x in itertools.islice(c, 10, 20): ... print(x)

  這里要著重強調的一點是?islice()?會消耗掉傳入的迭代器中的數據。 必須考慮到迭代器是不可逆的這個事實。 所以如果你需要之后再次訪問這個迭代器的話,那你就得先將它里面的數據放入一個列表中。

?

  四、跳過可迭代對象的開始部分

  你想遍歷一個可迭代對象,但是它開始的某些元素你并不感興趣,想跳過它們。

  首先介紹的是?itertools.dropwhile()?函數。使用時,你給它傳遞一個函數對象和一個可迭代對象。 它會返回一個迭代器對象,丟棄原有序列中直到函數返回Flase之前的所有元素,然后返回后面所有元素。

  假定你在讀取一個開始部分是幾行注釋的源文件。比如:

  

>>> with open('/etc/passwd') as f: ... for line in f: ... print(line, end='') ... ## # User Database # # Note that this file is consulted directly only when the system is running # in single-user mode. At other times, this information is provided by # Open Directory. ... ## nobody:*:-2:-2:Unprivileged User:/var/empty:/usr/bin/false root:*:0:0:System Administrator:/var/root:/bin/sh ... >>>

  如果你想跳過開始部分的注釋行的話,可以這樣做:

>>> from itertools import dropwhile >>> with open('/etc/passwd') as f: ... for line in dropwhile(lambda line: line.startswith('#'), f): ... print(line, end='')

  這個例子是基于根據某個測試函數跳過開始的元素。 如果你已經明確知道了要跳過的元素的個數的話,那么可以使用?itertools.islice()?來代替。比如:

>>> from itertools import islice >>> items = ['a', 'b', 'c', 1, 4, 10, 15] >>> for x in islice(items, 3, None): ... print(x) ... 1 4 10 15 >>>

  最后需要著重強調的一點是,以上方案適用于所有可迭代對象,包括那些事先不能確定大小的, 比如生成器,文件及其類似的對象。

?

  五、你想迭代遍歷一個集合中元素的所有可能的排列或組合

  1. itertools模塊提供了三個函數來解決這類問題。 其中一個是?itertools.permutations()?, 它接受一個集合并產生一個元組序列,每個元組由集合中所有元素的一個可能排列組成。 也就是說通過打亂集合中元素排列順序生成一個元組,比如:

>>> items = ['a', 'b', 'c'] >>> from itertools import permutations >>> for p in permutations(items): ... print(p) ... ('a', 'b', 'c') ('a', 'c', 'b') ('b', 'a', 'c') ('b', 'c', 'a') ('c', 'a', 'b') ('c', 'b', 'a') >>>

如果你想得到指定長度的所有排列,你可以傳遞一個可選的長度參數。就像這樣:

>>> for p in permutations(items, 2): ... print(p) ... ('a', 'b') ('a', 'c') ('b', 'a') ('b', 'c') ('c', 'a') ('c', 'b') >>>

?

  2.使用?itertools.combinations()?可得到輸入集合中元素的所有的組合。比如:

>>> from itertools import combinations >>> for c in combinations(items, 3): ... print(c) ... ('a', 'b', 'c')>>> for c in combinations(items, 2): ... print(c) ... ('a', 'b') ('a', 'c') ('b', 'c')>>> for c in combinations(items, 1): ... print(c) ... ('a',) ('b',) ('c',) >>>

  3.而函數?itertools.combinations_with_replacement()?允許同一個元素被選擇多次,比如:

>>> for c in combinations_with_replacement(items, 3): ... print(c) ... ('a', 'a', 'a') ('a', 'a', 'b') ('a', 'a', 'c') ('a', 'b', 'b') ('a', 'b', 'c') ('a', 'c', 'c') ('b', 'b', 'b') ('b', 'b', 'c') ('b', 'c', 'c') ('c', 'c', 'c') >>>

?

以上幾種是不是非常像我們數學課上所學的排列組合,注意,他們返回的都是可迭代對象。?  六、zip,chain函數應用 比如,假設你頭列表和一個值列表,就像下面這樣: headers = ['name', 'shares', 'price'] values = ['ACME', 100, 490.1]

  使用zip()可以讓你將它們打包并生成一個字典:

s = dict(zip(headers,values))

?

  你想在多個對象執行相同的操作,但是這些對象在不同的容器中,你希望代碼在不失可讀性的情況下避免寫重復的循環。

 itertools.chain()?方法可以用來簡化這個任務。 它接受一個可迭代對象列表作為輸入,并返回一個迭代器,有效的屏蔽掉在多個容器中迭代細節。 為了演示清楚,考慮下面這個例子:

  

>>> from itertools import chain >>> a = [1, 2, 3, 4] >>> b = ['x', 'y', 'z'] >>> for x in chain(a, b): ... print(x) ... 1 2 3 4 x y z

  itertools.chain()?接受一個或多個可迭代對象作為輸入參數。 然后創建一個迭代器,依次連續的返回每個可迭代對象中的元素。 這種方式要比先將序列合并再迭代要高效的多。

  

  七、你想將一個多層嵌套的序列展開成一個單層列表

  可以寫一個包含?yield?from?語句的遞歸生成器來輕松解決這個問題。比如:

from collections import Iterabledef flatten(items, ignore_types=(str, bytes)):for x in items:if isinstance(x, Iterable) and not isinstance(x, ignore_types):yield from flatten(x)else:yield xitems = [1, 2, [3, 4, [5, 6], 7], 8] # Produces 1 2 3 4 5 6 7 8 for x in flatten(items):print(x)

  語句?yield?from?在你想在生成器中調用其他生成器作為子例程的時候非常有用。 如果你不使用它的話,那么就必須寫額外的?for?循環了。比如:

def flatten(items, ignore_types=(str, bytes)):for x in items:if isinstance(x, Iterable) and not isinstance(x, ignore_types):for i in flatten(x):yield ielse:yield x

 

  注意:yield from 與 yield區別

yield from?后面需要加的是可迭代對象,它可以是普通的可迭代對象,也可以是迭代器,甚至是生成器。

使用yield

# 字符串 astr='ABC' # 列表 alist=[1,2,3] # 字典 adict={"name":"wangbm","age":18} # 生成器 agen=(i for i in range(4,8))def gen(*args, **kw):for item in args:for i in item:yield inew_list=gen(astr, alist, adict, agen) print(list(new_list)) # ['A', 'B', 'C', 1, 2, 3, 'name', 'age', 4, 5, 6, 7]

  使用yield from

# 字符串 astr='ABC' # 列表 alist=[1,2,3] # 字典 adict={"name":"wangbm","age":18} # 生成器 agen=(i for i in range(4,8))def gen(*args, **kw):for item in args:yield from itemnew_list=gen(astr, alist, adict, agen) print(list(new_list)) # ['A', 'B', 'C', 1, 2, 3, 'name', 'age', 4, 5, 6, 7]

  可以看出,yield from后面加上可迭代對象,他可以把可迭代對象里的每個元素一個一個的yield出來,對比yield來說代碼更加簡潔,結構更加清晰。

?

轉載于:https://www.cnblogs.com/jimmyhe/p/10825082.html

總結

以上是生活随笔為你收集整理的python死磕八之迭代器与生成器的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。