こんにちは(@t_kun_kamakiri)(‘◇’)ゞ
この記事ではPythonの「関数」について解説します。
関数の基本的な内容は前回の記事で解説をしております。
今回は、前回学んだ関数の続きをやります。
- ラムダ式:小さな処理を書く
- ジェネレーター:1つずつ取り出す処理(yield関数)
- デコレーター:関数に機能を追加・変更
Pytonの関数は以下の2通りを覚えておきましょう。
「def 関数名()」の基本的な書き方
「lambda パラメータ1, パラメータ2 : 処理」の書き方
☟書籍も紹介しておきます。
こちらの本がPython初心者が挫折することなく勉強できる本です。
(本記事のようのPython使用環境と異なりますが、とてもわかりやすいので全く問題ありません)
ラムダ式
わざわざ関数を作るまでもないときに、無名関数(関数に名前を付けない)方法で、関数を設定することができます。
それがラムダ式です。
書き方は以下です。
1 |
名前 = lambda 引数, 引数, ...: 式 |
実際に、defを使って関数を設定した場合、ラムダ式で関数を設定した場合を比較してみましょう。
defで関数を設定した場合
1 2 3 4 5 6 |
def times(a): return 2*a A = times(10) print('Aは{}です。'.format(A)) |
【結果】
1 |
Aは20です。 |
ふつうにaを2倍にするだけです。
これだけのためにわざわざ関数名に「times」という名前をつけているのでちょっとめんどうですよね。
ラムダ式で関数を設定した場合
1 2 3 |
A = lambda a : 2*a + 5 print('Aは{}です。'.format(A(10))) |
【結果】
1 |
Aは25です。 |
結果はdefを使った場合と同じです。
このように、ラムダ式を使うと「ささっ」っと関数に名前を付けることなく処理を書くことができます。
引数は2つ以上でも良い
関数に渡す引数は2つ以上でも構いません。
前回の記事でも計算した「薄肉円筒の応力」の計算についてです。
1 2 3 4 5 6 7 8 |
import math def calc(dia, press, t): sigma_theta = (press * dia)/(2.0 * t) return sigma_theta print('応力は{}Paです'.format(round(calc(50.0, 100.0, 1.2 ),2))) |
【結果】
1 |
応力は2083.33Paです |
↓こちらをラムダ式に書き換えてみましょう。
1 2 3 4 |
sigma_theta = lambda dia, press, t : (press * dia)/(2.0 * t) print(sigma_theta) print('応力は{}Paです'.format(round(sigma_theta(50.0,100.0,1.2),2))) |
【結果】
1 2 |
<function <lambda> at 0x7fb8117dc1e0> 応力は2083.33Paです |
sigma_theta自体はラムダ式のオブジェクトとして生成されています。
ラムダ式は複数行の処理を書く定義ができないので、複数行処理を書く場合は関数を使う方が良いでしょう。
map()関数をラムダ式で書く
別の例としてmap()関数をラムダ式で書いてみましょう。
map()関数は、以下のように書いてイテレータとして返ってきます。
1 |
map(関数, シーケンス型) |
シーケンスとは、複数の値を順番に並べたものをひとかたまりとして格納するための型のことです。
- リスト
- タプル
- range
- 文字列
では、実際にmap()関数を使ってコードを書いてみましょう。
defを使って書く場合
1 2 3 4 5 6 7 8 9 10 11 12 |
sample_list = list(range(5)) print('sample_list=', sample_list) def times(a): b = a*2 +10 return b sample_times_list = map(times, sample_list) print(sample_times_list) print('sample_times_list=', list(sample_times_list)) |
【結果】
1 2 3 |
sample_list= [0, 1, 2, 3, 4] <map object at 0x7efd9fd2bbe0> sample_times_list= [10, 12, 14, 16, 18] |
sample_listでリストを要します。
やりたいことをリスト内の要素を2倍して10足すということです。
そのために、わざわざ関数[times(a)]を用意して、
「sample_times_list = map(times, sample_list)」としています。
※ちなみにmap()関数で返ってくるのはイテレータです。
イテレータって何かというと「データの流れを表現するオブジェクト」を意味します。
つまり、オブジェクトなのですね。
なのでそのままでは要素の値がわからないので、「list(sample_times_list)」としてリストにしています。
ラムダ式で書く場合
1 2 3 4 5 6 7 8 |
sample_list = list(range(5)) print('sample_list=', sample_list) sample_times_list = map(lambda a: a*2 + 10, sample_list) print(sample_times_list) print('sample_times_list=', list(sample_times_list)) |
【結果】
1 |
sample_list= [0, 1, 2, 3, 4] <map object at 0x7efd9fd2bbe0> sample_times_list= [10, 12, 14, 16, 18] |
ラムダ式で書けば、defの関数を丸々書かなくても、map()関数の第一引数にラムダ式の関数を書くだけで良いのでコード量が少なくて済みます。
filter()関数をラムダ式で書く
filter()関数もラムダ式で書くととても便利です。
filter()関数も、map()関数同様に以下のように書いてイテレータで返ってきます。
1 |
filter(関数, シーケンス型) |
実際にコードを書いてみましょう。
1 2 3 4 5 6 |
sample_list = list(range(20)) print('sample_list=', sample_list) sample_list_even = filter(lambda x: x % 2 == 0, sample_list) print('sample_list_even=', list(sample_list_even)) |
【結果】
1 2 |
sample_list= [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19] sample_list_even= [0, 2, 4, 6, 8, 10, 12, 14, 16, 18] |
sample_listの要素の、「lambda x: x % 2 == 0」でTrueになったものだけを、sample_list_evenに代入しています。
最後に、リストにするためにlist(sample_list_even)としています。
ジェネレーター
次は、ジェネレーターの解説をします。
ジェネレータとは、Pythonのシーケンスを作成するオブジェクトのことですね。
- シーケンス:複数の値を順番に並べたものをひとかたまりとして格納するための型
- ジェネレータ:反復のたびにシーケンスの最後にどこを呼び出されていたかを記憶していて、次の値を返す
range()関数なんかがジェネレータ関数です。
言葉で説明してもよくわからないので、とりあえずコードを書いてみましょう。
普通にprint文でリスト型要素を出力
1 2 3 4 5 6 7 |
def timestep(list_time): for t in list_time: print('{}ms'.format(t)) list_data = [1.0 , 2.0 , 3.0 , 4.0 , 5.0] next_time = timestep(list_data) |
【結果】
1 2 3 4 5 |
1.0ms 2.0ms 3.0ms 4.0ms 5.0ms |
これにより、リスト型の要素がひとつひとつ取り出されたのがわかりますね。
print文をreturnにする
1 2 3 4 5 6 7 8 9 10 11 12 13 |
def timestep(list_time): for t in list_time: return '{}ms'.format(t) list_data = [1.0 , 2.0 , 3.0 , 4.0 , 5.0] next_time = timestep(list_data) print(next_time) print(next_time) print(next_time) print(next_time) print(next_time) |
【結果】
1 2 3 4 5 |
1.0ms 1.0ms 1.0ms 1.0ms 1.0ms |
結果は、リストの一つ目の要素だけが出力されました。
print文をyieldにする
先ほどのprint文のように、次の要素を出力する場合は、return文の代わりにyieldで戻り値を返すようにします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
def timestep(list_time): for t in list_time: yield '{}ms'.format(t) list_data = [1.0 , 2.0 , 3.0 , 4.0 , 5.0] next_time = timestep(list_data) print(next_time) print(next(next_time)) print(next(next_time)) print(next(next_time)) print(next(next_time)) print(next(next_time)) |
【結果】
1 2 3 4 5 6 |
<generator object timestep at 0x7efd9fcc6d00> 1.0ms 2.0ms 3.0ms 4.0ms 5.0ms |
「next_time」というジェネレータオブジェクトが生成されているので、next()関数を使って次の値、その次の値・・・・・と順番に出力することができます。
デコレーター
デコレータとは、既存のソースコードを書き換えずに、関数に機能を追加したり変更したりできる機能のことです。
まずは、デコレータを使わない例から見てみましょう。
デコレータを使わない例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
def hello_add(func): def wrapper(): res = func() print("今日はいい天気ですね!") return res return wrapper def hello(): print('おはようございます。') hello = deco(hello) hello() |
【結果】
1 2 |
おはようございます。 今日はいい天気ですね! |
def hello()でhello()関数を定義して、「おはようございます。」と出力するようにしています。
def hello_add(func)で何か関数を渡して、
print(“今日はいい天気ですね!”)
を追加する処理を書いています。
最後に、hello = deco(hello)でhello()関数を上書きしています。
デコレータを使う例
これをデコレーターを使うと以下のようになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
def hello_add(func): def wrapper(): res = func() print("今日はいい天気ですね!") return res return wrapper @hello_add def hello(): print('おはようございます。') hello() |
【結果】
1 2 |
おはようございます。 今日はいい天気ですね! |
ちょっと記述が簡単になったのがわかりますかね?
変更点は以下です。
1 2 3 4 5 6 7 8 9 |
def hello(): print('おはようございます。') hello = deco(hello) #以下に変更 @hello_add def hello(): print('おはようございます。') |
「@hello_add」を書く代わりに、「hello = deco(hello)」のコードが不要になりました。
まとめ
今回は、以下の解説を行いました。
- ラムダ式:小さな処理を書く
- ジェネレーター:1つずつ取り出す処理(yield関数)
- デコレーター:関数に機能を追加
おすすめ参考書
[…] 【Python初心者】 関数のlamda式、ジェネレーター、デコレーターを理解しよう。 […]