【Python】Pythonで自作関数を作ろう【基本講座】
Pythonでコードを書いていくうちに、同じ処理を何回も書いてしまっていることはありませんか?プログラミングにおいて、その行為は間違っています。
正確に言うとコードエラーは出ませんが、正しいコーディングではありません。
なぜでしょうか?理由は簡単です。その処理んい修正が入ったときあなたはどうしますか?その処理を100か所以上書いてあったら一個ずつ直しますか?また、直し忘れがないことを確約できますか?あなた以外にもそのコードを触るプロジェクトだった時、抜けなく修正できますか?答えはNOじゃないでしょうか?
関数とは、そんなヒューマンエラーを回避するためにも重要な役割を担っております。是非取得してください。
Pythonで自作関数を作ってみよう!
基本書式 def
Pythonで扱う自作関数はすべてdef文から始まります。
C++などから来た人には違和感があるかもしれませんがこれだけです。
さらに、普通、関数には波括弧{}で定義された部分を覆っているのですが、Pythonにはありません。インデントという空白を空けることで、namespaceを保っているのです。
これが他の言語の関数と一番の違うところでしょう。
Pythonの関数は以下のように記述します。
1 2 |
def hoge(a, b): return a*b |
ポイントは、先ほど言ったインデントの他に、def文の終わりに「:」をつけること。返し値を持つときは、returnで任意の変数を返すこと。引数に関しては、いつもの感じなので分かるかと思います。
また、関数名の命名についてですが、通常すべて小文字にします。また、複数の単語から構成される名前を付ける場合、「_」で繋げます。これをスネークケースと呼びます。
キーワード引数
下記のような例を見てください。
1 2 3 4 5 6 7 8 |
def hoge(a, b): return a*b c = hoge(100, 200) print(c) >> 20000 |
関数を呼び出して返り値を変数に代入していますが、すぐ上に関数が定義されているので、どれが何の引数かわかりますが、引数が多かったり、関数が離れた箇所にあったとき、どれがどの引数か分からなくなりませんか?
Pythonでは、キーワード引数というのを指定でき、それを解決しています。
1 2 3 4 5 6 7 8 |
def hoge(a, b): return a*b c = hoge(a=100, b=200) print(c) >> 20000 |
どうですか?分かりやすくなったでしょう。
このキーワード引数を使用するにあたって1点注意があります。
通常の引数(固定引数)とキーワード引数を混在させる場合です。その場合、キーワード引数を後に記述するというルールがあるので注意しましょう。
まちがえると「SyntaxError: positional argument follows keyword argument」が出ます。
Pythonの関数 おもしろ話
下記のような例の時、printされる値はいくらでしょうか?
1 2 3 4 5 6 7 8 |
def hoge(a): a += 1 b = 10 hoge(b) print(b) |
11 だと思いましたか?正解は、10です。
return がないからでしょ?とプログラムをかじったことのある人なら思うかもしれませんが、どういうカラクリで呼び出し側には反映されてないのかご存知でしょうか?
Pythonでは関数を呼び出して引数を受け渡す場合に、オブジェクトを指し示す「リファレンス」と呼ぶ値がコピーされて渡されています。正確に言うと、実引数のオブジェクトID番号が仮引数にコピーされて渡されています。この時、関数内部で何かしら処理をすると、オブジェクト番号が変更されます。そのため別のものとみなされ呼び出し側に反映されていないのです。
そのためreturnで返す必要があります。なお、オブジェクトIDが変わらないときは、その値が適用されます。
さて、それは、どんな時でしょうか?
以前、ミュータブル(変更可能)・イミュータブル(変更不可能)のお話をしたと思います。ここでは、それが深くかかわっており、引数にリストなどのミュータブルなオブジェクトを渡して、値を追加・変更などしたときは呼び出し側の値も変更されています。その理由は先ほど述べた通りオブジェクトIDが実引数と同じだからです。
例を見てみましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
def hoge(a): print("a:{}".format(id(a))) a[0] = 0 a.append(100) print("a:{}".format(id(a))) l = [1, 2, 3] hoge(l) print(l) >> a:2247346804872 a:2247346804872 [0, 2, 3, 100] |
たしかにオブジェクトIDが同じですね。実プロジェクトで関数の返り値がないのに値が変わっているというのはこういった理由があるのです。
ただし、例外があります。
関数内で新しいオブジェクトに代入してしまうとオブジェクトIDが変わってしまうので注意が必要です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
def hoge(a): print("a:{}".format(id(a))) a = a + [3, 5] print("a:{}".format(id(a))) l = [1, 2, 3] hoge(l) print(l) >> a:2247346806408 a:2247346806472 [1, 2, 3] |
新しいオブジェクトを足さなければ値は反映されます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
def hoge(a): print("a:{}".format(id(a))) a += [3, 5] print("a:{}".format(id(a))) l = [1, 2, 3] hoge(l) print(l) >> a:2247346804872 a:2247346804872 [1, 2, 3, 3, 5] |
変数スコープについて
Pythonでもローカル変数やグローバル変数があります。使い方は、C言語などと同じです。グローバル変数は、関数外部で値を代入した変数でグローバルスコープであり、任意の関数からアクセスできます。
また、関数内部で値を代入した変数のスコープは、ローカルスコープといい関数内部がスコープとなります。関数の外部からアクセスできません。
そのため、ローカル変数とグローバル変数は同じ名前を付けても干渉しませんが、実際問題ややこしくなるのでやめましょう。
関数の返り値について
Pythonの返り値は、なかなか便利で1関数にひとつ返すだけではありません。
カンマでつなげばいくつでも返すことが可能です。
1 2 3 4 5 6 7 8 |
def test(): return 'abc', 100 t = test() print(type(t)) >> <class 'tuple'> |
このようにすることで返せます。サンプルでは、printでtypeを表示させているのは、複数の返し値のとき、タプル型で返るという点を知っておいてください。
次の例を見てください。
1 2 3 4 5 6 7 8 9 10 11 12 |
def test(): return 'abc', 100, [0, 1, 2] a, b, c = test() print(a) print(b) print(c) >> abc 100 [0, 1, 2] |
関数の返り値を複数受け取りたい時は、上記のようにカンマで変数を連結させると順に取り出せます。実は、この形は、タプル型の()の省略形です。タプル型は()の省略が可能なのです。ただ、曖昧さを省くため可能であれば付けた方が良いかもしれません。
可変長引数について
関数を定義したとき、引数をどれだけとるか不定の時ってありますよね?
そんなとき、可変長引数を使用します。
下記のように定義します。
1 2 3 4 5 6 |
def hoge(arg, *args): print(arg, args) hoge(1,2,3,4) >> 1 (2, 3, 4) |
引数に*をつけることで、可変長引数になります。そして、可変長引数はタプル型で返します。
また、可変長引数に**と記述すると辞書型として受け取ることができます。
1 2 3 4 5 6 |
def hoge(**kwargs): print(kwargs) hoge(name="a.a", age=1) >> {'name': 'a.a', 'age': 1} |
引数を付ける順番が存在します。
arg → *args → **kwargs
この順番でないとSyntacks Errorが出ますので注意が必要です。
Pythonおすすめ書籍
最初の2冊は初心者向けです。Effectiv Pythonはこの2冊を終えてから読むとかなりわかりやすく感動できると思います。
|
|
|