pythonベースの4

14155 ワード

このブログのすべての文章はただブロガーがメモを取るためのもので、ブログの内容は詳しくありません(後で暇があれば修正して改善します)、思考も跳躍するところがあります.ブログの内容を詳しく勉強したいなら、文章の後ろの参考リンクを参考にして、勉強を楽しんでください.

ジェネレータと反復


ビルダー


リスト生成式により、リストを直接作成できます.ただし、メモリの制限を受けると、リストの容量は限られているに違いありません.また、100万個の要素を含むリストを作成すると、大きなストレージスペースを消費するだけでなく、前のいくつかの要素にアクセスするだけで、後ろのほとんどの要素が消費するスペースが無駄になります.
だから、リスト要素が何らかのアルゴリズムで推定できるなら、ループの過程で後続の要素を絶えず推定することができますか?これにより、listを完全に作成する必要がなくなり、大量のスペースを節約できます.Pythonでは,このように循環しながら計算するメカニズムをジェネレータ:generatorと呼ぶ.
generatorを作成するには、さまざまな方法があります.1つ目の方法は簡単で、リスト生成式の[]を()に変更するだけでgeneratorが作成されます.
>>> L = [x*x for x in range(10)]
>>> L
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> g=(x*x for x in range(10))
>>> g
 at 0x036B28D0>
>>> 

Lとgの違いは、最外層の[]と()のみであり、Lはlistであり、gはgeneratorである.generatorの次の戻り値はnext()関数で取得できます.
>>> next(g)
0
>>> next(g)
1
>>> next(g)
4
>>> next(g)
9
>>> next(g)
16
>>> next(g)
25
>>> next(g)
36
>>> next(g)
49
>>> next(g)
64
>>> next(g)
81
>>> next(g)
Traceback (most recent call last):
  File "", line 1, in <module>
StopIteration

generatorは、next(g)を呼び出すたびにgの次の要素の値を計算し、最後の要素まで計算し、より多くの要素がない場合にStopIterationのエラーを投げ出すアルゴリズムを保存します.

yield


yieldについて
The yield statement is only used when defining a generator function, and is only used in the body of the generator function. Using a yield statement in a function definition is sufficient to cause that definition to create a generator function instead of a normal function. When a generator function is called, it returns an iterator known as a generator iterator, or more commonly, a generator. The body of the generator function is executed by calling the generator’s next() method repeatedly until it raises an exception. When a yield statement is executed, the state of the generator is frozen and the value of expression_list is returned to next()’s caller. By “frozen” we mean that all local state is retained, including the current bindings of local variables, the instruction pointer, and the internal evaluation stack: enough information is saved so that the next time next() is invoked, the function can proceed exactly as if the yield statement were just another external call. The yield statement is not allowed in the try clause of a try … finally construct. The difficulty is that there’s no guarantee the generator will ever be resumed, hence no guarantee that the finally block will ever get executed.
generatorと関数の実行プロセスは違います.関数は順番に実行され、return文または最後の行の関数文に遭遇すると返されます.generatorの関数となり、next()を呼び出すたびに実行され、yield文が返され、再実行されると前回返された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")

yield from


python 3新規追加内容
yield from expression, allowing a generator to delegate part of its operations to another generator. This allows a section of code containing yield to be factored out and placed in another generator. Additionally, the subgenerator is allowed to return with a value, and the value is made available to the delegating generator.
While designed primarily for use in delegating to a subgenerator, the yield from expression actually allows delegation to arbitrary subiterators.
For simple iterators, yield from iterable is essentially just a shortened form of for item in iterable: yield item
>>> def g(x):
    yield from range(x, 0, -1)
    yield from range(x)
... ... ... 
>>> list(g(5))
[5, 4, 3, 2, 1, 0, 1, 2, 3, 4]

However, unlike an ordinary loop, yield from allows subgenerators to receive sent and thrown values directly from the calling scope, and return a final value to the outer generator
>>> def accumulate():
...     tally = 0
...     while 1:
...         next = yield
...         if next is None:
...             return tally
...         tally += next
...
>>> def gather_tallies(tallies):
...     while 1:
...         tally = yield from accumulate()
...         tallies.append(tally)
...
>>> tallies = []
>>> acc = gather_tallies(tallies)
>>> next(acc)  # Ensure the accumulator is ready to accept values
>>> for i in range(4):
...     acc.send(i)
...
>>> acc.send(None)  # Finish the first tally
>>> for i in range(5):
...     acc.send(i)
...
>>> acc.send(None)  # Finish the second tally
>>> tallies
[6, 10]

The main principle driving this change is to allow even generators that are designed to be used with the send and throw methods to be split into multiple subgenerators as easily as a single large function can be split into multiple subfunctions.

反復器


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と呼びます.
>>> 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の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.http://www.cnblogs.com/alex3714/articles/5765046.html 2. https://docs.python.org/3/whatsnew/3.3.html#pep-380 3. http://www.cnblogs.com/wupeiqi/articles/4980620.html