40のおっさんのPython学習記録

20年以上前、学部の必修のC言語が全く理解できずに同級生に放り投げ、その後コーディングから遠ざかったガチ文系のおっさんが、ふと思い立ってPythonに挑戦しています。

新たな気づき(Chapter 11-2)

おっさんです。最近明らかにアクティビティが低下しています。Pythonに飽きているというわけではありません。2月は旅行に行って1週末つぶしたり、別の週末は金曜日に飲みすぎたせいで週末をひどい二日酔いで潰したりしているせいです。おっさんなのでそもそも二日酔いになりやすい上に、ほんの少し疲れているだけで、この程度で酔わへんやろと言う量で泥酔するようになりましあt。最近、おっさんは酒は毒だなと言う思いを日に日に強くしています。今週末と来週末も長期海外出張で潰すので、また停滞しそうです。海外出張中に現地オサレカフェにMacBook持ち込んでやればいいだろとも思いますが、海外出張中って基本的には疲れ果ててるので、ホテルでだらけるしか僕にはもう選択肢はありません。それと、もう一つマジな話としては、前回のみんpyでも激しく躓いた関数とクラスのあたりに入っているので、どうしても理解に苦しんでいるので進捗が遅くならざるをえない局面というのはありますな。さて、今週末も張り切っていきましょう。

  • イテラブルとは、要素を順に取り出せるオブジェクト。リスト、タプル、辞書、文字列。セット・集合は順番がないからイテラブルではない。
  • イテレータは、要素を1個取り出すごとにどこまで取り出したかという情報を保持。
  • iter(iterable)でイテラブルからイテレータを作ることができる。
  • next(iterator)で要素を取り出す。1度目で1要素目、2度目で2要素目、3度目で3要素目が取り出される。全ての要素を取り出し終えると繰り返さず、その後はエラーになる。以下の様に3つしか要素がない場合4度目をやるとエラーになる。
>>> colors = ['red','yellow','green']
>>> color_iter=iter(colors)
>>> next(color_iter)
'red'
>>> next(color_iter)
'yellow'
>>> next(color_iter)
'green'
>>> next(color_iter)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
  • ジェネレータ関数を定義してイテレータを作成することも可能。ジェネレーター関数はyieldで値を返す。
def signal_generator():
    yield 'red'
    yield 'green'
    yield 'yellow'

signal = signal_generator()

print(next(signal))
print(next(signal))
print(next(signal))
print(next(signal))

for-inの利用も可能なので、全ての要素を一発で取り出すことも可能。

  • returnもyieldも関数の定義の中で値を返すのは同じ役割ではあるが、returnの場合は関数の処理を全て終了し値を返すが、yieldは関数の処理を一旦停止し値を返し、次のきっかけがあるまでは何の処理も値を返したりもしないので、メモリ処理量が少なく住む。
  • ジェネレーター関数を用いることで、文字通り数列を順に生成することができる。
def num_generator():
    n=0
    while True:
        num=n**2
        yield num
        n+=1

def do_something(num):
    return (num,num%2,num%3)

gen=num_generator()
for i in range(1,10):
    num=next(gen)
    result=do_something(num)
    print(result) #1回目〜10回目の計算結果が表示される

print(result) #10回目の計算結果が再度表示される

num=next(gen)
result=do_something(num)
print(result) #11回目の計算結果が表示される

print(result) #11回目の計算結果が再度表示される
  • 一番外の囲み記号を()とすることで、ジェネレータ式を書くことができる。リストにするとまとめて全部出せる。こんな感じ。
>>> square_gen = (i**2 for i in range(0,10))
>>> next(square_gen)
0
>>> next(square_gen)
1
>>> next(square_gen)
4
>>> next(square_gen)
9
>>> list(square_gen)
[16, 25, 36, 49, 64, 81]
  • ジェネレータは値を取り出すだけではなく、コルーチンという仕組みを使って外部から値を送信することも可能。
def coro():
    hello = yield "Hello" #yieldで値を返すだけではなく、sendされた値が代入される
    yield hello
    yield hello

c = coro()
print(next(c))         #Hello
print(c.send("World")) #World
print(next(c))         #World
print(next(c))         #StopIterationエラー

上の例だと、hello〜の行含めて3行しかないので、4回目のprintはエラーになる。

  • サブジェネレーターというか、ジェネレーターの中にジェネレーターをぶら下げることもできる。
def main_gen(n):
    yield from range(n,0,-1)
    yield from "abc"
    yield from [10,20,30]
    yield from sub_gen()

def sub_gen():
    yield 'x'
    yield 'y'
    yield 'z'

gen=main_gen(3)

print(next(gen)) #3
print(next(gen)) #2
print(next(gen)) #1
print(next(gen)) #a
print(next(gen)) #b
print(next(gen)) #c
print(next(gen)) #10
print(next(gen)) #20
print(next(gen)) #30
print(next(gen)) #x
print(next(gen)) #y
print(next(gen)) #z
print(next(gen)) #StopIteration