pythonジェネレータと反復器の詳細
5904 ワード
リスト生成:
例1:
a = [i+1 for i in range(10)] print(a)
出力:
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
例2:
L = [1, 2, 3, 4, 5] print([i*i for i in L if i>3])
出力:
[16, 25]
例3:
L = [1, 2, 3, 4, 5] I = [6, 7, 8, 9, 10] print([i*a for i in L for a in I if i > 2 if a < 8])
出力:
[18, 21, 24, 28, 30, 35]
ビルダー:
リスト生成式により、リストを直接作成できます.ただし、メモリの制限を受けると、リストの容量は限られているに違いありません.また、100万個の要素を含むリストを作成すると、大きなストレージスペースを消費するだけでなく、前のいくつかの要素にアクセスするだけで、後ろのほとんどの要素が消費するスペースが無駄になります.
したがって、リスト要素が何らかのアルゴリズムに従って推定できる場合、完全なlistを作成する必要がなく、大量のスペースを節約することができます.Pythonでは,このように循環しながら計算するメカニズムをジェネレータ:generatorと呼ぶ.
generatorを作成するには、さまざまな方法があります.1つ目の方法は簡単で、リスト生成式の[]を()に変更するだけでgeneratorが作成されます.
例:
L = [1, 2, 3, 4, 5] I = [6, 7, 8, 9, 10] g = (i*a for i in L for a in I ) print(g)
出力:
at 0x00000276586C1F48>
Lとgの違いは、最外層の[]と()のみであり、Lはlistであり、gはgeneratorである.
リストの各要素を直接印刷することができ、generatorのnext()メソッドで
next(g)
例1:
L = [1, 2, 3, 4, 5] I = [6, 7, 8, 9, 10] g = (i*a for i in L for a in I ) print(next(g)) print(next(g)) print(next(g))
出力:
6 7 8
例2:
L = [1, 2, 3, 4, 5] I = [6, 7, 8, 9, 10] g = (i*a for i in L for a in I if i > 2 if a < 8) print(next(g)) print(next(g)) print(next(g))
出力:
18 21 24
generatorはアルゴリズムを保存しているため、next(g)を呼び出すたびにgの次の要素の値が計算され、最後の要素まで計算され、より多くの要素がない場合、StopIterationのエラーが放出されます.generatorも反復可能なオブジェクトであるため、forループを使用するのが正しいです.
例3:
g = (i*i for i in range(0, 5)) for i in g: print(i)
generatorを作成すると、next()メソッドは基本的に呼び出されず、forループで反復されます.
generatorはとても強いです.推定アルゴリズムが複雑で,リスト生成式のようなforループでは実現できない場合,関数で実現することも可能である.
例えば、有名なフィボラッチ数列(Fibonacci)は、最初の数と2番目の数を除いて、いずれの数も前の2つの数から加算することができます.
1, 1, 2, 3, 5, 8, 13, 21, 34, ...
フィポラチ数列はリスト生成式では書けませんが、関数で印刷するのは簡単です.
def fib(max): n, a, b = 0, 0, 1 while n < max: print b a, b = b, a + b n = n + 1
上の関数は、フィボナッチ数列の最初のN個の数を出力することができます.
>>> fib(6) 1 1 2 3 5 8
よく見るとfib関数は実際にはフィポラチ数列の推定規則を定義しており,最初の要素から後続の任意の要素を推定することができ,この論理はgeneratorに非常に類似していることがわかる.すなわち,上の関数はgeneratorとは一歩しか離れていない.fib関数をgeneratorにするにはprint(b)をyield bに変更するだけです.
def fib(max): n, a, b = 0, 0, 1 while n < max: yield b a, b = b, a + b n = n + 1 return 'done' print(fib(5))
出力:
呼び出し方法:##しかしforループでgeneratorを呼び出すと、##generatorのreturn文##の戻り値が得られないことがわかります.戻り値を取得するには、StopIterationエラーを取得する必要があります.戻り値はStopIterationのvalueに含まれます.
for i in fib(5): print(i)
出力:
1 1 2 3 5
または、
date = fib(5) print(date.__next__()) print(date.__next__()) print(date.__next__()) print('test') print(date.__next__()) print(date.__next__())
出力:
1 1 2 test 3 5
sendメソッドには、前回保留されたyield文の戻り値を指定するパラメータがあります.
また、yieldにより、単一スレッドの場合に同時演算を実現する効果も実現できます.
反復器:
forループに直接作用できるデータ型は、次のとおりです.
1つのクラスはリスト、tuple、dict、set、strなどの集合データ型である.
1つのクラスはgeneratorであり、ジェネレータとyield付きgenerator functionを含む.
これらのforループに直接作用するオブジェクトは、反復可能なオブジェクト:Iterableと総称されます.
isinstance()を使用して、オブジェクトがIterableオブジェクトであるかどうかを判断できます.
>>> from collections import Iterable >>> isinstance([], Iterable) True >>> isinstance({}, Iterable) True >>> isinstance('abc', Iterable) True >>> isinstance((x for x in range(10)), Iterable) True >>> isinstance(100, Iterable) False
ジェネレータはforループだけでなくnext()関数によって呼び出され、次の値を返し、最後にStopIterationエラーが投げ出されて次の値を返すことができなくなるまで返すことができます.
*next()関数によって呼び出され、次の値を繰り返し返すオブジェクトを反復器:Iteratorと呼びます.
isinstance()を使用して、オブジェクトがIteratorオブジェクトであるかどうかを判断できます.
>>> from collections import Iterator >>> isinstance((x for x in range(10)), Iterator) True >>> isinstance([], Iterator) False >>> isinstance({}, Iterator) False >>> isinstance('abc', Iterator) False
ジェネレータはすべてIteratorオブジェクトですが、list、dict、strはIterableですが、Iteratorではありません.
リスト、dict、strなどのIterableをIteratorにするにはiter()関数を使用します.
>>> isinstance(iter([]), Iterator) True >>> isinstance(iter('abc'), Iterator) True
なぜlist、dict、strなどのデータ型がIteratorではないのですか?
これは、PythonのIteratorオブジェクトがデータストリームを表しているためです.Iteratorオブジェクトはnext()関数によって呼び出され、データがないときにStopIterationエラーが投げ出されるまで次のデータを返し続けることができます.このデータストリームは秩序あるシーケンスと見なすことができるが,シーケンスの長さを事前に知ることはできず,next()関数を介して次のデータをオンデマンドで計算するしかないため,Iteratorの計算は不活性であり,次のデータを返す必要がある場合にのみ計算される.
Iteratorは、全体の自然数など、無限大のデータストリームを表すこともできる.リストを使用すると、自然数全体を格納することは永遠に不可能です.
まとめ:
forループに作用するオブジェクトはすべてIterableタイプです.
next()関数に作用するオブジェクトはIteratorタイプであり、不活性計算のシーケンスを表す.
リスト、dict、strなどの集合データ型はIterableですが、Iteratorではありませんが、iter()関数でIteratorオブジェクトを取得できます.
Python 3のforループは本質的にnext()関数を絶えず呼び出すことによって実現され、例えば:
for x in [1, 2, 3, 4, 5]: pass
実際には、
#Iteratorオブジェクトを最初に取得:it=iter([1,2,3,4,5])#ループ:while True:try:#次の値を取得:x=next(it)except StopIteration:#StopIterationに遭遇するとループbreakを終了
例1:
a = [i+1 for i in range(10)] print(a)
出力:
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
例2:
L = [1, 2, 3, 4, 5] print([i*i for i in L if i>3])
出力:
[16, 25]
例3:
L = [1, 2, 3, 4, 5] I = [6, 7, 8, 9, 10] print([i*a for i in L for a in I if i > 2 if a < 8])
出力:
[18, 21, 24, 28, 30, 35]
ビルダー:
リスト生成式により、リストを直接作成できます.ただし、メモリの制限を受けると、リストの容量は限られているに違いありません.また、100万個の要素を含むリストを作成すると、大きなストレージスペースを消費するだけでなく、前のいくつかの要素にアクセスするだけで、後ろのほとんどの要素が消費するスペースが無駄になります.
したがって、リスト要素が何らかのアルゴリズムに従って推定できる場合、完全なlistを作成する必要がなく、大量のスペースを節約することができます.Pythonでは,このように循環しながら計算するメカニズムをジェネレータ:generatorと呼ぶ.
generatorを作成するには、さまざまな方法があります.1つ目の方法は簡単で、リスト生成式の[]を()に変更するだけでgeneratorが作成されます.
例:
L = [1, 2, 3, 4, 5] I = [6, 7, 8, 9, 10] g = (i*a for i in L for a in I ) print(g)
出力:
at 0x00000276586C1F48>
Lとgの違いは、最外層の[]と()のみであり、Lはlistであり、gはgeneratorである.
リストの各要素を直接印刷することができ、generatorのnext()メソッドで
next(g)
例1:
L = [1, 2, 3, 4, 5] I = [6, 7, 8, 9, 10] g = (i*a for i in L for a in I ) print(next(g)) print(next(g)) print(next(g))
出力:
6 7 8
例2:
L = [1, 2, 3, 4, 5] I = [6, 7, 8, 9, 10] g = (i*a for i in L for a in I if i > 2 if a < 8) print(next(g)) print(next(g)) print(next(g))
出力:
18 21 24
generatorはアルゴリズムを保存しているため、next(g)を呼び出すたびにgの次の要素の値が計算され、最後の要素まで計算され、より多くの要素がない場合、StopIterationのエラーが放出されます.generatorも反復可能なオブジェクトであるため、forループを使用するのが正しいです.
例3:
g = (i*i for i in range(0, 5)) for i in g: print(i)
generatorを作成すると、next()メソッドは基本的に呼び出されず、forループで反復されます.
generatorはとても強いです.推定アルゴリズムが複雑で,リスト生成式のようなforループでは実現できない場合,関数で実現することも可能である.
例えば、有名なフィボラッチ数列(Fibonacci)は、最初の数と2番目の数を除いて、いずれの数も前の2つの数から加算することができます.
1, 1, 2, 3, 5, 8, 13, 21, 34, ...
フィポラチ数列はリスト生成式では書けませんが、関数で印刷するのは簡単です.
def fib(max): n, a, b = 0, 0, 1 while n < max: print b a, b = b, a + b n = n + 1
上の関数は、フィボナッチ数列の最初のN個の数を出力することができます.
>>> fib(6) 1 1 2 3 5 8
よく見るとfib関数は実際にはフィポラチ数列の推定規則を定義しており,最初の要素から後続の任意の要素を推定することができ,この論理はgeneratorに非常に類似していることがわかる.すなわち,上の関数はgeneratorとは一歩しか離れていない.fib関数をgeneratorにするにはprint(b)をyield bに変更するだけです.
def fib(max):
n,a,b = 0,0,1
while n < max:
#print(b)
yield b
a,b = b,a+b
n += 1
return 'done'
これがgeneratorを定義する別の方法です.関数定義にyieldキーワードが含まれている場合、この関数は通常の関数ではなくgeneratorです.def fib(max): n, a, b = 0, 0, 1 while n < max: yield b a, b = b, a + b n = n + 1 return 'done' print(fib(5))
出力:
呼び出し方法:##しかしforループでgeneratorを呼び出すと、##generatorのreturn文##の戻り値が得られないことがわかります.戻り値を取得するには、StopIterationエラーを取得する必要があります.戻り値はStopIterationのvalueに含まれます.
for i in fib(5): print(i)
出力:
1 1 2 3 5
または、
date = fib(5) print(date.__next__()) print(date.__next__()) print(date.__next__()) print('test') print(date.__next__()) print(date.__next__())
出力:
1 1 2 test 3 5
sendメソッドには、前回保留されたyield文の戻り値を指定するパラメータがあります.
また、yieldにより、単一スレッドの場合に同時演算を実現する効果も実現できます.
#_*_coding:utf-8_*_
__author__ = 'Alex Li'
import time
def consumer(name):
print("%s !" %name)
while True:
baozi = yield
print(" [%s] , [%s] !" %(baozi,name))
def producer(name):
c = consumer('A')
c2 = consumer('B')
c.__next__()
c2.__next__()
print(" !")
for i in range(10):
time.sleep(1)
print(" 2 !")
c.send(i)
c2.send(i)
producer("alex")
ジェネレータによるコモンパラレル演算反復器:
forループに直接作用できるデータ型は、次のとおりです.
1つのクラスはリスト、tuple、dict、set、strなどの集合データ型である.
1つのクラスはgeneratorであり、ジェネレータとyield付きgenerator functionを含む.
これらのforループに直接作用するオブジェクトは、反復可能なオブジェクト:Iterableと総称されます.
isinstance()を使用して、オブジェクトがIterableオブジェクトであるかどうかを判断できます.
>>> from collections import Iterable >>> isinstance([], Iterable) True >>> isinstance({}, Iterable) True >>> isinstance('abc', Iterable) True >>> isinstance((x for x in range(10)), Iterable) True >>> isinstance(100, Iterable) False
ジェネレータはforループだけでなくnext()関数によって呼び出され、次の値を返し、最後にStopIterationエラーが投げ出されて次の値を返すことができなくなるまで返すことができます.
*next()関数によって呼び出され、次の値を繰り返し返すオブジェクトを反復器:Iteratorと呼びます.
isinstance()を使用して、オブジェクトがIteratorオブジェクトであるかどうかを判断できます.
>>> from collections import Iterator >>> isinstance((x for x in range(10)), Iterator) True >>> isinstance([], Iterator) False >>> isinstance({}, Iterator) False >>> isinstance('abc', Iterator) False
ジェネレータはすべてIteratorオブジェクトですが、list、dict、strはIterableですが、Iteratorではありません.
リスト、dict、strなどのIterableをIteratorにするにはiter()関数を使用します.
>>> isinstance(iter([]), Iterator) True >>> isinstance(iter('abc'), Iterator) True
なぜlist、dict、strなどのデータ型がIteratorではないのですか?
これは、PythonのIteratorオブジェクトがデータストリームを表しているためです.Iteratorオブジェクトはnext()関数によって呼び出され、データがないときにStopIterationエラーが投げ出されるまで次のデータを返し続けることができます.このデータストリームは秩序あるシーケンスと見なすことができるが,シーケンスの長さを事前に知ることはできず,next()関数を介して次のデータをオンデマンドで計算するしかないため,Iteratorの計算は不活性であり,次のデータを返す必要がある場合にのみ計算される.
Iteratorは、全体の自然数など、無限大のデータストリームを表すこともできる.リストを使用すると、自然数全体を格納することは永遠に不可能です.
まとめ:
forループに作用するオブジェクトはすべてIterableタイプです.
next()関数に作用するオブジェクトはIteratorタイプであり、不活性計算のシーケンスを表す.
リスト、dict、strなどの集合データ型はIterableですが、Iteratorではありませんが、iter()関数でIteratorオブジェクトを取得できます.
Python 3のforループは本質的にnext()関数を絶えず呼び出すことによって実現され、例えば:
for x in [1, 2, 3, 4, 5]: pass
実際には、
#Iteratorオブジェクトを最初に取得:it=iter([1,2,3,4,5])#ループ:while True:try:#次の値を取得:x=next(it)except StopIteration:#StopIterationに遭遇するとループbreakを終了