nprogram’s blog

気ままに、プログラミングのトピックについて書いていきます

Python文法学習 (関数、クロージャー、ジェネレーター、ラムダ式)

はじめに

Pythonを学ぶため、Pythonの基礎を本ページに記載します。

関数

関数の引数は、型がないので、注意が必要です。attack関数の引数は、文字列なのです。

パラメーター(引数)が1つの場合

def add_sound(attack):
    for i in range(5):                  # 5回繰り返す
        print(attack, '-->パコーン')    # パラメーターの値に擬音を付けて表示
    return attack


add_sound('スマッシュ')

パラメーター(引数)が複数の場合

def calculate_add(a, b):
    result_calculate = a + b
    return result_calculate


result = calculate_add(3, 5)

print(result)

デフォルトパラメーターも使えます

def calculate_add(a=1, b=2):
    result_calculate = a + b
    return result_calculate


#result = calculate_add(3, 5)
result = calculate_add()
print(result)

可変長パラメーターも使えます

# パラメーター名の前にアスタリスク(*)を付けると可変長パラメーターとなります
def sequence_sound(*args):
    for a in(args):
        print(a)
    print(args)


sequence_sound('ポーン', 'パコーン', 'スコーン')

[実行イメージ]

ポーン
パコーン
スコーン
('ポーン', 'パコーン', 'スコーン')

キーと値がセットになったパラメーター

「** パラメーター名」と書くとそのパラメーターは辞書型になります。

# パラメーター名の前にアスタリスク(*)を付けると可変長パラメーターとなります
def attacks(**kwargs):
    print(kwargs)


attacks(volley='ポーン', smash='パコーン')

パラメーターで関数を取得して、これを実行する

# パラメーターで関数を取得し、これを実行する関数を定義
# 関数をパラメーターで取得したり、戻り値として返す関数を高階関数と呼ぶ

def attack_sound(a, s):
    print(a, '-->', s)


def run_something(func, arg1, arg2):
    func(arg1, arg2)


run_something(attack_sound, 'ドロップショット', 'ポワーン')

# ドロップショット --> ポワーン

関数内関数の定義

関数の中で関数を定義できます。複雑な処理を内部の関数に任せることで、コードの重複を避けることができます。

EX. 整数値の例

def outer(a, b):
    def inner(c, d):
        return c + d
    return inner(a, b)


result = outer(1, 5)

print(result)

print(outer(1, 5))

関数内関数の定義2 (文字列を扱う場合)

関数のパラメーター名strokeと関数内関数のパラメーター名sが異なるようにします。 同一の場合は、コンパイラーによっては、警告を受けます。

def add_sound(stroke):

    # 関数内関数
    def inner(s):
        return s + ' --> ' + 'ぱっこーん'

    return inner(stroke)     # 関数内関数の結果を返す


result = add_sound('フォアハンドストローク')

print(result)

# フォアハンドストローク --> ぱっこーん

クロージャー

クロージャーとは、引数をセットして、関数を呼び出すコードを作っておいて、それをあとで実行できるようにするもの 関数を呼び出すパターンがあらかじめわかっているなら、それを記録しておいて、必要なときに実行するといった使い方ができます 次は、先の関数内関数をクロージャーにしたものです。

def add_sound(stroke):

    # クロージャーを定義する前に、呼び出すとエラーになるため、注意
    # inner()

    # クロージャー
    def inner():
        return stroke + ' --> ' + 'ぱっこーん'

    print(inner())

    return inner     # 関数内関数の結果を返す


result = add_sound('フォアハンドストローク')

print(result())

# フォアハンドストローク --> ぱっこーん
# フォアハンドストローク --> ぱっこーん

関数内関数とクロージャーは以下の点が異なります。 inner()にはパラメーターがなく、代わりに外側の関数のstrokeパラメーターを直接使う add_sound()は、inner()関数の処理結果を返すのではなく、関数名(関数オブジェクト)を返す。

inner()関数はadd_sound()に渡されたstrokeにアクセスすることができ、これを覚えておくことができます。 これがクロージャーの重要なポイントです。

一方、add_sound()関数は、戻り値として、inner()を関数オブジェクトとして、返します。 strokeに渡す値を引数にしてadd_sound()を実行すれば、storkeの値を保持したinner()関数のオブジェクトが返されます。 この関数オブジェクトがすなわちクロージャーです。

def add_sound(stroke):

    # クロージャーを定義する前に、呼び出すとエラーになるため、注意
    # inner()

    # クロージャー
    def inner():
        return stroke + ' --> ' + 'ぱっこーん'

    return inner     # 関数内関数の結果を返す


a = add_sound('フォアハンドストローク')
b = add_sound('バックハンドストローク')

print(a())
print(b())


# フォアハンドストローク --> ぱっこーん
# バックハンドストローク --> ぱっこーん

クロージャーaとbは、自分たちが作られたときに使われていたstrokeの内容を覚えています。あとは、実行したいタイミングでクロージャーを呼び出す。

ラムダ式

高階関数を用いた実現例は以下のようになります。

# 高階関数 (パラメータ
def edit_sounds(sounds, func):
    for sound in sounds:
        print(func(sound))


# パラメーターで取得した値の末尾に感嘆符を追加する関数
def impact(sound):
    return sound + '!!!'


pattern = ['ポーン', 'ぱっこーん', 'ビシ']

edit_sounds(pattern, impact)

# 実行イメージ
#  ポーン!!!
# ぱっこーん!!!
# ビシ!!!

次に上をラムダ式で表現します。 ラムダ式を用いることで、impact関数が必要無くなりました。 ラムダ式は、名前のない処理部だけの関数であるため、無名関数と呼ばれる。

# 高階関数 (パラメータ
def edit_sounds(sounds, func):
    for sound in sounds:
        print(func(sound))


# パラメーターで取得した値の末尾に感嘆符を追加する関数
def impact(sound):
    return sound + '!!!'


pattern = ['ポーン', 'ぱっこーん', 'ビシ']

edit_sounds(pattern, lambda sound: sound + '!!!')

# 実行イメージ
#  ポーン!!!
# ぱっこーん!!!
# ビシ!!!

ジェネレーター

ジェネレーターとは、Pythonのシーケンスを作成するオブジェクトのこと。ジェネレーターオブジェクトでは、戻り値をreturnではなくyieldで返す関数で生成することができます。反復処理のrange()もジェネレーター関数。

ジェネレーターでは、反復処理のたびに、最後に呼び出されたときに、シーケンスのどこを指していたか覚えていて、次の値を返します。

一方、通常の関数は、以前の呼び出しについて何も覚えていません。

def generate(str):
    for a in str:
        yield '[' + a + ']'


gen = generate('ぱっこーん!')

print(next(gen))

print(next(gen))

print(next(gen))

print(next(gen))

print(next(gen))

print(next(gen))

# print(next(gen)) 文字列以上に処理を行うとするとエラーになるため、注意

# 実行イメージ
# [ぱ]
# [っ]
# [こ]
# [ー]
# [ん]
# [!]

イテレート(反復処理)が可能なので、forステートメントを使ったほうが簡単です。

def generate(str):
    for a in str:
        yield '[' + a + ']'


gen = generate('ぱっこーん!')

for s in gen:
    print(s)

# 実行イメージ
# [ぱ]
# [っ]
# [こ]
# [ー]
# [ん]
# [!]