Python/関数

出典: フリー教科書『ウィキブックス(Wikibooks)』
ナビゲーションに移動 検索に移動

初歩的な内容[編集]

関数[編集]

いくつかの処理をまとめたものを、プログラミングでは「関数」(かんすう)といいます。数学でいう「関数」とは、意味がちがうので、気をつけてください。

なお英語では、数学の「関数」のことを function (ファンクション)といいます。プログラミング用語の「関数」のことも英語では function といいます。

もとの英語をみれば、どちらにも「数」にあたる英語(number 「ナンバー」など)が無いことに、注目してください。

プログラミングの「関数」では、かならずしも、数(整数や小数などの「数」)をつかう必要は、ありません。


さて、pythonでは、ユーザーが自分で関数をつくることができます。ユーザーによる関数の作成には「def」を用います。「def」とは、おそらく定義を意味する英語 definition の頭3文字です。


コード例
def sagyou():
    print("a")
    print("b")

sagyou()
sagyou()
実行結果
a
b
a
b

このように、繰り返し作業をするときなど、関数が便利です。

関数の定義の書式は、

def 関数名():
    処理内容

というふうになります。

関数名のあとに、コロン記号「:」をつけるのを、忘れないようにしてください。

関数の内容は、インデントが終わるまでです。

たとえば下記の関数sagyou()の場合、

def sagyou():
    print("a")
    print("b")

print("c")
sagyou()

関数の内容は、

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

までです。

print("c")は、関数sagyou()の内容ではないのです。

引数のある関数[編集]

(コード例)
def kansu(a):
    print("計算するよ")
    b = a * 2
    print(a,"を2倍したら",b)

a = 1
kansu(a)

a = 5
kansu(a)

a = 80
kansu(a)

(実行結果↓)

計算するよ
1 を2倍したら 2
計算するよ
5 を2倍したら 10
計算するよ
80 を2倍したら 160

ある処理を繰り返し行いたい場合、関数をつかうことで、記述をへらせます。 関数に作業させる際、関数外で定義された変数を、上記のように利用する事ができ、そのような変数を「引数」(ひきすう)といいます。


上記のコードのうち、関数の定義部分は、

def kansu(a):
    print("計算するよ")
    b = a * 2
    print(a,"を2倍したら",b)

↑ この範囲が、関数の定義部分です。


引数のある関数の定義の書式は、

def 関数名(引数):
    処理内容

というふうになります。

「引数」は「ひきすう」と読みます。


引数が2個以上の場合[編集]

def menseki(a,b):
    print("面積を計算するよ")
    print(a * b)

a = 3
b = 5

menseki(a,b)


結果
面積を計算するよ
15


下記のように、呼び出し時に引数の値を「menseki(3,5)」のように指定することもできます。

def menseki(a,b):
    print("面積を計算するよ")
    print(a * b)

menseki(3,5)


結果
面積を計算するよ
15


やや難しい内容[編集]

関数のローカル性[編集]

a = 10
b = 11

def kansu(a):
    print("計算するよ")
    b = a * 2
    print(a,"を2倍したら",b)

kansu(a)
print("bは",b)
実行結果
計算するよ
10 を2倍したら 20
bは 11

変数bは、関数の中で使われており、関数のなかでbに20が代入されています。

にもかかわらず、関数終了後に、bを呼び出して表示すると、もとの値の11のままです。


このようにpythonでは、関数内で数を置きかえる動作は、原則として、関数外の動作に影響を与えません。


どのような仕組みになっているかというと、関数内で数値が書き換えされた際、pythonでは同名の別変数を新しく作っているのです。(C言語の関数のローカル処理とは、pythonは仕組みが違います。)

pythonではid()という組み込み関数で、変数のid番号(C言語でいう「アドレス」のようなもの)を見る事が出来ますので、確認してみましょう。


a = 10
b = 11

print("aのid",id(a))
print("bのid",id(b))


def kansu(a):
    print("関数の処理中")
    print("aのid",id(a))
    print("計算するよ")
    b = a * 2
    print(a,"を2倍したら",b)
    print("bのid",id(b))
    print("関数の終了")

kansu(a)
print("bは",b)

print("aのid",id(a))
print("bのid",id(b))
実行結果
aのid 140336460552064
bのid 140336460552096
関数の処理中
aのid 140336460552064
計算するよ
10 を2倍したら 20
bのid 140336460552384
関数の終了
bは 11
aのid 140336460552064
bのid 140336460552096

ただし、このように新しいidを用意してくれる場合は、置き換えようとしてる変数が整数や浮動小数といった場合です。

リストの場合、新規のidを用意してくれず、関数内でリストを置きかえると、関数外のもとのリストの内容も変化します。(リストについては『python/リスト』を参照せよ。)



グローバル変数?の使い方

Python において、関数の外で定義した変数を関数内で使う場合には、必ず、関数の定義文の引数、および呼び出し文の引数に、その変数を記述しなればなりません。

コード例
a = 10

def kansu(a): # kansu() だとエラー
    print("計算するよ")
    a = a * 2
    print("答えは", a)

kansu(a) # kansu() だとエラー
実行結果
計算するよ
答えは 20
計算するよ
答えは 20


C言語などと違い、Pythonでは関数を呼び出すたびに、前回の実行結果はリセットされます。なので上記コードでは、2回とも答えが「20」です。(けっして2回目の答えが2倍の「40」にならない。)



戻り値[編集]

def sagyou():
    print("a")
    print("b")
    return 0

sagyou()
sagyou()
実行結果
a
b
a
b

pythonでは、あまり実用性がないのですが、関数の終了の際に何らかの値を出すことができ、そのような値を 戻り値(もどりち) と言います。

上記のコードでは「0」が戻り値です。return の直後に、戻り値を書きます。

戻り値の0は、特に表示指定しないかぎり、表示されません。


returnがあると、その行で関数が終了とみなされます。


たとえば、もし

def sagyou():
    print("a")
    return 0
    print("b")

sagyou()
sagyou()

とプログラムすると、

実行結果
a
a

のようになります。bは表示されなくなってしまいます。



下記のように引数を使って、計算結果を関数外部に渡すこともできます。しかしpythonでは、この方法は実は、あまり必要ありません。

def kansu(a):
    print("計算するよ")
    b = a * 2
    return b

a = 1
print(a,"を2倍したら",kansu(a))

a = 5
print(a,"を2倍したら",kansu(a))

a = 80
print(a,"を2倍したら",kansu(a))

なぜ、戻り値を渡す方法が、あまり必要ないのかというと、戻り値を渡す方法を使った場合、たとえば上記のコードのように、何回もprint文を書くハメになってしまいます。


他のプログラム言語では、戻り値を「0」とする事により、エラーなく関数が終了した事と扱う慣習があります。(エラーの場合には戻り値が「-1」など、戻り値を0以外の特定の数にする慣習がある。)


キーワード引数[編集]

たとえば、

ミカンが1個あたり100円、リンゴが1個あたり150円としましょう。

買い物のプログラムを書くとしましょう。

(※ まだキーワード引数を使ってません。)

def kaimono(mikan, ringo):
    print("買い物")
    syuppi = 100 * mikan + 150 * ringo
    print("ミカンを", mikan, "個、買います。", "リンゴを", ringo, "個、買います。")
    print(syuppi, "円の出費です。")


kaimono(1, 2)

kaimono(3, 1)


実行結果
買い物
ミカンを 1 個、買います。 リンゴを 2 個、買います。
400 円の出費です。
買い物
ミカンを 3 個、買います。 リンゴを 1 個、買います。
450 円の出費です。

べつに上記コードでもいいのですが、 第三者が「kaimono(3,1)」だけ見た場合、3と1のどちらがリンゴの個数で、どちらがミカンの個数か、分からないかもしれません。

なので、下記のように、関数を使う際に、引数欄に「変数名=値」というように引数名を併記する書式で、どの引数にどの値を代入するかを指定して書けます。

(キーワード引数を使ったコードです。)

def kaimono(mikan, ringo):
    print("買い物")
    syuppi = 100 * mikan + 150 * ringo
    print("ミカンを", mikan, "個、買います。", "リンゴを", ringo, "個、買います。")
    print(syuppi, "円の出費です。")


kaimono(mikan=1, ringo=2)

kaimono(mikan=3, ringo=1)


pythonでは、引数のこういう記法を、キーワード引数と言います。

実行結果
買い物
ミカンを 1 個、買います。 リンゴを 2 個、買います。
400 円の出費です。
買い物
ミカンを 3 個、買います。 リンゴを 1 個、買います。
450 円の出費です。

キーワード引数は、下記のように、引数の順序を入れ替えても平気です。

def kaimono(mikan, ringo):
    print("買い物")
    syuppi = 100 * mikan + 150 * ringo
    print("ミカンを", mikan, "個、買います。", "リンゴを", ringo, "個、買います。")
    print(syuppi, "円の出費です。")


kaimono(ringo=2, mikan=1)


実行結果
買い物
ミカンを 1 個、買います。 リンゴを 2 個、買います。
400 円の出費です。

関数内関数[編集]

基本[編集]

Pythonでは、関数の中に、別の関数の定義文を書くことが簡単にできます。

つまり、関数の内部に、「内部関数」(「関数内関数」ともいう)を書くことができます。

python用語では、関数の中の関数のことを「クロージャ」といいます。


コード例
def sagyou():
    print("a")
    
    # 関数内関数
    def naibu():
        print("fgh") 
        print(123 + 400) 
    # ここまで関数内関数
        
    # ここはもう関数内関数の外
    naibu() # これは単なる関数の呼び出し
    
    print("b")

    
sagyou()


実行結果
a
fgh
523
b


なお、C言語には、関数内関数(内部関数)の機能は無いです。このように関数内関数は、わりかし、新しいプログラミング言語の流行の機能のひとつです。

C言語では、単に関数の中から、別の関数の呼び出しをするだけなら可能です。しかし、C言語では、関数の中に、別の関数の定義文を書くことは(C言語では)できません。


Python以外の言語では、PHPやD言語にも関数内関数(内部関数)の機能があります。


戻り値についての注意[編集]

次の関数を見てみると、一見すると再帰(さいき)命令のように見えて誤解しがちですが、しかし実はまったく再帰していません。

なお、再帰(さいき)とは、呼び出し元をもういちど呼び出す命令のことで、たとえば階乗計算 (5×4×3×2×1 のような計算)で使われるものです。


コード例
def soto():
    
    # 関数内関数
    def naibu():
        return 5
           
    return naibu()
    
print(soto())


実行結果
5

def naibu() の数行あとに、return naibu() とあるので、あたかも naibu() が return naibu() を返却しているかのように誤解しがちです。

しかし、誤解です。よく見てください。return naibu() があるのは、def naibu のスコープの外側です。


つまり、このコードは、けっして再帰命令ではなく、単に、関数内関数にある戻り値(上記コードの「5」)を、そのまま外側の関数でも戻り値として流用すると宣言しているにすぎません。

なお、外側の関数の戻り値の命令

return naibu()

は、けっして単に戻り値だけを指定しているのではなく、さらに、関数 naibu() を実行する事も、実は宣言しています。pythonはそういう仕様なのです。

なので、たとえばコメントアウトして

# return naibu() 

のようにすると、その後のprint()命令の中身が無いので、

none

などの表示がされます。


まとめ

つまり、関数内関数にある

return 関数内関数名()

とは、次の2つの処理を行っています。

  • その関数内関数を実行する。
  • さらに、その関数内関数の引数を外部関数でも流用する。

という働きです。


その他の話題[編集]

Pythonの場合、関数の外で定義された変数を関数の中で使う場合には、引数を記述しないといけないので、関数内関数にて、関数外の変数を使いたい場合、たとえば下記コードのようになります。

コード例
a = 10

def soto(a): # soto() だとエラー    

    # 関数内関数
    def naibu(a):
        print ("utigawa")
        b = a + 2
        return b
           
    return naibu(a) # これは soto(a) に所属している行
    
print(soto(a))
実行結果
utigawa
12

上記コードには実用性がありませんが(わざわざ関数内関数を使う必要が無い)、分かり易さのため、コードを書いています。


デコレータ[編集]

基本[編集]

デコレータという、関数の前後に処理を付け足す機能がpythonにあります。

デコレータに該当するのは、下記のコードの@マークの部分とその直後の関数の定義内容です。

デコレーターのコーディングでは、下記のように関数内関数の記法を流用する仕様です。

関数を飾り立てる(デコレーション)する・修飾するという意味で、このような機能をデコレータと言います。


書式をまとめると、おおむね下記のようになります。

書式
def デコレータ名(デコレータ用の引数): 
    def 内部関数名(*args, **kwargs):
        // 前処理 //
        res = hikisu(*args, **kwargs)
        // 後処理 // 
        return res
    return 内部関数名

@デコレータ名
def 修飾される側の関数(その引数):
    修飾される側の関数のもともとの処理


コード例
def okikae(hikisu): # 置き換え(引数)のつもり
    def naibu(*args, **kwargs):
        print("計算します。計算結果を出力します")
        res = hikisu(*args, **kwargs)
        print("終了します。")
        return res
    return naibu

@okikae
def keisan(a, b):
    print(a + b)
    
keisan(3,5)


実行結果
計算します。計算結果を出力します
8
終了します。


解説
res = hikisu(*args, **kwargs)

の hikisu の引数の部分は (*args, **kwargs) だと、仕様でそう決まっています。

デコレータは関数内関数の仕組みを流用している機能なので、上記コードのように関数内関数の記法で書かないといけないとか、引数が決まっていたりとか、なんだか、あまり普遍的でない規則があります。

なお、もし関数内関数の内側の宣言だけ取り出してコードを下記のように

# エラーになる例
def naibu(*args, **kwargs):
    print("計算します。計算結果を出力します")
    res = hikisu(*args, **kwargs)
    print("終了します。")
    return res


@okikae
def keisan(a, b):
# 以下略

のように書いても、エラーになります。

デコレータは、文法と思うよりも、便利なライブラリが最初から組み込んであるとでも思ってください。


2つ以上のデコレータ[編集]

デコレータは、1つの関数に複数のデコレータを修飾する事もできます。

コード例
# 1つめのデコレータ。無視されてしまう。
def okikae(hikisu):
    def naibu(*args, **kwargs):
        print("計算します。計算結果を出力します")
        res = hikisu(*args, **kwargs)
        print("終了します。")
        return res
    return naibu

# 2つめのデコレータ
def betu(hikisu):
    def naibu(*args, **kwargs):
        print("lets keisan")
        res = hikisu(*args, **kwargs)
        print("finish")
        return res
    return naibu

@okikae
@betu
def keisan(a, b):
    print(a + b)
    
keisan(3,5)


実行結果
計算します。計算結果を出力します
lets keisan
8
finish
終了します。


なお、下記のように@を2カ所に分けて書いた場合、最後に実行されたデコレータの内容が優先されます。

コード例
# 1つめのデコレータ。無視されてしまう。
def okikae(hikisu):
    def naibu(*args, **kwargs):
        print("計算します。計算結果を出力します")
        res = hikisu(*args, **kwargs)
        print("終了します。")
        return res
    return naibu

@okikae
def keisan(a, b):
    print(a + b)


# 2つめのデコレータ
def betu(hikisu):
    def naibu(*args, **kwargs):
        print("lets keisan")
        res = hikisu(*args, **kwargs)
        print("finish")
        return res
    return naibu

@betu
def keisan(a, b):
    print(a + b)
    
keisan(3,5)


実行結果
lets keisan
8
finish

高度な機能[編集]

ジェネレータ[編集]

イテレータ[編集]