前書き
for i in [1,2,3,4,5,6]:
print(i)
これは非常に単純なfor in
ステートメントです。反復にはどのような種類のオブジェクトを使用できますか?
コンテナ、反復可能オブジェクト、イテレータ
Pythonでは、すべてがオブジェクトであり、オブジェクトの抽象化がクラスであり、オブジェクトのコレクションがコンテナーです。
リストlist:[0,1,2]
、タプルtuple:(0,1,2)
、辞書dict:{0:0,1:1,2:2}
、およびコレクションset:set{0,1,2}
はすべてコンテナーです。コンテナは、さまざまな要素を組み合わせたものとして想像できます。異なるコンテナの違いは、内部データ構造の実装方法にあります。次に、さまざまなシナリオに応じて、時間計算量と空間計算量が異なる適切なコンテナを選択できます。
イテレータはnext
メソッドを提供します。このメソッドを呼び出すと、コンテナ内の次のオブジェクトを取得したり、エラーメッセージを表示したりできますStopiteration
。辞書やコレクションなどのコンテナは順序付けられていないため、リストのように要素のインデックスを指定する必要はありません。 、インデックスはありません。以前のブログ投稿<Pythonの基本的な知識の組み合わせ-辞書とコレクション>で述べたように、辞書の基礎となる実装が採用されてい哈希表
ます。次の関数がコンテナ内のすべての要素を1つずつ取得できることを知っておく必要があります。
反復可能なオブジェクトの場合、関数iter()
を介してイテレータを返し、next()
関数をトラバースすることfor in
もそのようなプロセスですが、非表示になっています。
isinstance(obj,Iterable)
オブジェクトが反復可能かどうかは、Pythonの組み込みを介して判断できます。
def is_iterable(func):
try:
iter(func)
return '可以'
except Exception:
return '不可以'
func = [
1234,
'1234',
[1,2,3,4],
(1,2,3,4),
{
1:1,2:2,3:3,4:4},
{
1,2,3,4},]
for fun in func:
print('{} 是否可迭代\t\t {}'.format(fun,is_iterable(fun)))
# 输出
1234 是否可迭代 不可以
1234 是否可迭代 可以
[1, 2, 3, 4] 是否可迭代 可以
(1, 2, 3, 4) 是否可迭代 可以
{
1: 1, 2: 2, 3: 3, 4: 4} 是否可迭代 可以
{
1, 2, 3, 4} 是否可迭代 可以
この例を通して、番号1234を除くすべてのオブジェクトが反復可能であることがわかります。
ビルダー
ジェネレーターはシンプルで軽量なイテレーターです
たとえば、このようなイテレータ[i for i in range(1000000)]
は100万個の数値を含むリストを生成できますが、各要素は生成後にメモリに格納されますが、メモリが不足するとOOM(OutOfMemeryError)エラーが発生します。
一度に大量のデータを生成するためにイテレーターが必要ない場合、ジェネレーターの役割が明らかになります。next()
関数を呼び出している場合にのみ、次の変数が生成されます。Pythonで(i for i in range(10000000))
ジェネレーターを初期化するために使用されます。
ジェネレーターはイテレーターのように多くのメモリーを消費せず、使用時にのみ呼び出されます。ジェネレーターは初期化時に生成された操作を実行する必要がないため、メモリー使用量はそれよりもはるかに少なくなります。イテレータ。
カスタムジェネレーター
例1:恒等式
数学にはアイデンティティがあります(1+2+3+···+n)^2 = 1^3 + 2^3 + 3^3+···+n^3
。今では、ジェネレータを使用してこの式を実装しています。
def generator(num):
i = 1
while True:
yield i ** num
i += 1
gen_1 = generator(1)
gen_3 = generator(3)
def get_sum(n):
sum1 , sum2 = 0, 0
for i in range(n):
num_n = next(gen_1)
result = next(gen_3)
print('num_n = {},result = {}'.format(num_n,result))
sum1 += num_n
sum2 += result
get_sum(10)
# 输出
num_n = 1,result = 1
num_n = 2,result = 8
num_n = 3,result = 27
num_n = 4,result = 64
num_n = 5,result = 125
num_n = 6,result = 216
num_n = 7,result = 343
num_n = 8,result = 512
num_n = 9,result = 729
num_n = 10,result = 1000
このコードのgenerator()
関数のyieldキーワードは、実際には魔法のメソッドです。この関数の戻り値は、実際にはジェネレーターです。関数がこの部分まで実行されると、一時停止して飛び出します。next(gen_)
関数が呼び出されると、関数はyield
後で実行を続けると、ローカル変数の値は1のままではなく、1に基づいて1ずつ増加し、その結果から、num_n
1から10、およびresult
1から1000に変化します。
例2:リスト内の番号の位置を見つける
リストを指定して、リスト内でこの番号の位置を見つけます
従来の方法
L = [1, 6, 2, 4, 5, 2, 3, 4, 6, 3, 3]
def find_element(list,target):
result = []
for location, num in enumerate(list):
if num == target:
result.append(location)
return result
result = find_element(L,3)
print(result)
# 输出
[6, 9, 10]
従来の方法では、ライブラリ関数を使用して関数enumerate()
のインデックスを返し、リストに追加します
イテレータ
L = [1, 6, 2, 4, 5, 2, 3, 4, 6, 3, 3]
def find_element_use_generator(list,target):
for location, num in enumerate(list):
if num == target:
yield location
result = find_element_use_generator(L,3)
print(list(result))
# 输出
[6, 9, 10]
違いは明らかですがyield
、戻り値は、出力する前にリストに変換するGenerator
必要があるオブジェクトですlist
。
例3:サブシーケンスを決定する
2つのシーケンスが与えられた場合、最初のシーケンスが2番目のシーケンスのサブシーケンスであるかどうかを判別します。
この問題を解決するには:従来のアルゴリズムは欲張りアルゴリズムであり、リストの先頭から開始して2番目のシーケンスをスキャンする2つのポインターを維持します。最初の番号が最初のポインターと同じである場合、最初のポインターさらに、最初のポインタが最初のシーケンスの最後の要素をオーバーフローしたTrue
場合は、を返しFalse
ます。それ以外の場合はを返します。
次に、イテレーターとジェネレーターを使用してこのアルゴリズムを完成させます。
def is_subsequence(list_a,list_b):
list_b = iter(list_b)
return all(i in list_b for i in list_a)
print(is_subsequence([1,2,4],[1,2,3,4,5]))
print(is_subsequence([1,4,3],[1,2,3,4,5]))
# 输出
True
False
アイデア:
iter(list_b)
目的は、list_b
負荷をイテレータに変更することですfor i in list_a
オブジェクトをトラバースするために使用されるジェネレーターですa
- オブジェクトを移動する際
a
場合i
も本b
与えられたとき、i
また、中に存在する場合b
非常に巧妙に、ジェネレーターの特性を使用しnext()
て、次のように、関数の実行時に現在のポインターが保存されます。
b = (i for i in range(5))
print(2 in b)
print(4 in b)
print(3 in b)
# 输出
True
True
False
明らかに、保存されたポインタの場合、print(3 in b)
明らかにFalse
a。
総括する
この記事で言及されている4つの異なる構造があります:①コンテナ、②反復可能オブジェクト、③イテレータ、④ジェネレータ
- コンテナは
iter()
イテレータオブジェクトです。イテレータは関数を使用してイテレータをnext()
取得でき、イテレータは関数を介して次の要素を取得してトラバーサルをサポートできます。 - Generatorは特別なイテレーターであり、軽量でシンプルなバージョンのイテレーターです。Generatorは、コードを明確にし、メモリ使用量を削減し、プログラム構造を最適化し、プログラムの実行速度を向上させることができます。
- ジェネレーターは、Python2バージョンでコルーチンを実装するための重要な方法です。Python3.5以降、新しいasyncawit構文糖衣構文が導入され、コルーチンのジェネレーターの実装は遅れています。
ブログ投稿のフォローアップ更新については、私の個人ブログをフォローしてください:Stardust Blog