今回は関数のカリー化とジェネレータについて紹介する。

カリー化

既存の関数の引数を部分的に適用した、新しい関数を作ることをカリー化と言う。

例として、以下のようなシンプルな関数があったとする。

def add_numbers(x, y):
    return x + y

二つの引数をとり、合計した値を返すという関数だ。

この関数を使って、引数を一つしか持たない全く新しい関数を作成してみる。

def add_numbers(x, y):
    return x + y

add_two = lambda x: add_numbers(x, 2)
add_two(3) # 5

4行目のコードに注目してくれ。
既存のadd_numbers関数を利用し、引数を一つしか取らないラムダ関数(無名関数)を定義している。
このadd_numbersの一つ目の引数xはカリー化された状態にある。

また、Python組み込みのfunctoolsモジュールのpartial関数を使えば、上記のコードをもっとシンプルに書くことができる。

def add_numbers(x, y):
    return x + y

from functools import partial

add_two = partial(add_numbers, 2)
add_two(3) # 5

ジェネレータとイテレータ

ジェネレータを説明する前に、まずはイテレータを理解しておく必要があるのでそちらを先に説明しよう。

イテレータとは

イテレータは、リストやディクショナリのような連続した値を順番に処理するもので、イテレータプロトコルを使ってこの機能が実現される。

some_dict = {'a': 1, 'b': 2, 'c': 3}

dict_iterator = iter(some_dict)
dict_iterator # <dict_keyiterator at 0x108d04048>

例えば、some_dictに対しfor文でループ処理をすると、イテレータプロトコルを使って、変数keyにsome_dictのキーが連続して渡される。

some_dict = {'a': 1, 'b': 2, 'c': 3}

for key in some_dict:
    print(key)

ジェネレータとは

ジェネレータは、新しいイテレータを作成する方法で、複数の一連した結果を遅延して返すことができる。
ジェネレータの作り方は、関数内でreturnの代わりにyieldキーワードを使用するだけだ。

def func_squares(n=10):
    for i in range(1, n + 1):
        yield i ** 2
        
func_squares() # <generator object func_squares at 0x10893c5e8>

5行目でジェネレータを呼び出しているが、この時点ではコードは実行されない。

for文など、ジェネレータの要素にリクエストして初めてコードは実行されるのだ。

def func_squares(n=10):
    for i in range(1, n + 1):
        yield i ** 2
        
for x in func_squares():
    print(x, end=', ') # 1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 

まとめ

Pythonプログラミング初心者の俺は、まだカリー化やジェネレータなど、実践で使ったことはない。

正直いうと代替の手段で何とかなるので使ってこなかったのだが、プログラミング上級者はコードの簡略化などにこだわって、このような機能も積極的に利用しているのだろう。

自身のステップアップのためにも学んだ機能はどんどん取り入れた方が良いのだろう。