def文(関数)

ここまで練習問題も含めると type(), print(), round()や変数の型を変換するint(), float(), str(), bool()などの組み込み関数を紹介しました。これらは、予めpythonに組み込まれている関数なので、「組み込み関数」と呼ばれています。
関数は組み込み関数以外にも自分で定義して使うことができます。ここでは、defを使って関数を作成してみましょう。
関数化の目的は、コードをブロック化して関数化することにより、全体のコードの量を減らし見易くかつ考え易くすることになり、結果としてミスが減らすことにあります。

例題_2_4 関数を作る

price_without_tax (税抜き価格)と tax_rate(税率)から税込価格を計算する関数 price_incl_tax を作成してみます。

def price_incl_tax(price_without_tax, tax_rate):     #def 関数名(引数):
    result = price_without_tax * (1 + tax_rate/100)  #定義記載範囲は半角空白4文字分のインデント
    return round(result)                             #return 戻り値
price_incl_tax(100, 8)

108

最初のセルで def を使用してprice_incl_taxという名前の関数を定義(define)しています。関数名の後に丸括弧()を置き、その中に引数としてprice_without_tax と tax_rate を記載し、最後にコロン(:)を置きます。次の行から始まる関数の定義範囲はif文の時と同様に、半角空白4文字又はタブ1個分のインデントを入れて記載します。今回の様に戻り値(税込価格)がある場合には return の後に記載します。
この定義文のセルを実行しても一見何も起こりませんが、実際はprice_incl_taxという関数が定義されています。それを確認するために二つ目のセルで price_incl_tax(100, 8) と実行してみましょう。すると最初のセルで定義した関数を使って戻り値の8%税込価格である108.0が出力されます。

一度関数を定義すればそのファイルをシャットダウンするまでは、何度もこの関数を使用することができます。

foods = 1000
goods = 2000
price = price_incl_tax(foods, 8) + price_incl_tax(goods, 10)
price

3280

組み込み関数とは違い、ユーザが定義した関数は、ファイルをシャットダウンしたり、jupyter labのコマンドモードで 00 (カーネルリスタート)をした後で、def文を実行せずに関数price_incl_taxを使用するとNameErrorとなります。

発生し易いエラー NameErrorSyntaxErrorIndentationError

練習問題_2_10(練習問題_2_3の関数化)

前の章(if~elif~else文)の練習問題_2_3 のご自分の解答を使って、game_score(x, y)という関数を作成して下さい。
引数は矢が当たった座標(x, y)、戻り値は score です。また実際の座標を入力して score を確認するコードも作成して下さい。

ヒントはこちら
解答例はこちら

引数の書き方

関数を呼び出す際の引数の書き方は、以下の様に=を使って引数に代入することも可能です。この様に=を使って記載する引数をキーワード引数と言います。

foods = 1000
price_incl_tax(price_without_tax = foods, tax_rate = 8)

1080

キーワード引数を使用すれば、以下の様に引数同士の順番を逆にしても構いません。引数の数が多い時には間違いを避けるためにもこちらの書き方をするほうが良いでしょう。

foods = 1000
price_incl_tax(tax_rate = 8, price_without_tax = foods)

1080

キーワード引数ではなく、引数の位置に意味を持たせてオブジェクトのみを記載した引数を位置引数と言います。位置引数とキーワード引数の両方を使用する場合は、必ずキーワード引数を位置引数の後に置かなければなりません。
例えば price_incl_tax(tax_rate = 8, 1000) とした場合は
SyntaxError: positional argument follows keyword argument
つまり、位置引数がキーワード引数の後ろにあるSyntaxErrorとなります。

引数のデフォルト設定

以下の様にdef文の引数にデフォルト値(初期値、この場合 tax_rate=10)を設定することができます。tax_rateの引数が省略された場合はデフォルト値の10%が適用されます。

def price_incl_tax(price_without_tax, tax_rate = 10):      #tax_rateのデフォルト値を10に設定
    result = price_without_tax * (1 + tax_rate/100) 
    return round(result)
price_incl_tax(1000)

1100

引数のデフォルト値を設定する際の注意点として、デフォルト値を設定した引数の後ろにデフォルト値を設定していない引数を置いた場合以下の様にSyntaxErrorが発生します。

print関数のデフォルト設定

前章のprint関数の説明の箇所で、sepとendというオプションについて以下の様な説明をしました。
これらはデフォルトがついた引数、それぞれ sep = ‘ ‘, end = ‘\n’ というデフォルトがついた引数が、print関数に設定されていたことによるものです。

print関数をマニュアルで確認すると、以下の様に引数を含めて記載されています。
print(*objects, sep=’ ‘, end=’\n’, file=sys.stdout, flush=False)
*objects の様に引数の先頭に * を付加することにより任意の個数のオブジェクトを引数に格納することができる様になります。これらのオブジェクトは次章で紹介する tuple に格納されて関数に引き渡されます。また * がついた引数の後の引数は、全て = を使ったキーワード引数で記載する必要があります。

これらキーワード引数の意味は、sep は オブジェクト間を埋める文字でデフォルトは半角スペース、endは最後の文字でデフォルトは改行、fileは出力するファイル指定でデフォルトのsys.stdoutは標準出力でjupyter labの場合はコードボックスの下に出力することを意味しています。最後の flush ですが、print関数はfileに依存してある程度データをバッファに蓄積した後に出力しますが、flush = True とすることによりバッファを飛ばして即時的に出力することができます。

辞書を使った引数

def function(**args): の様に ** をつけることにより、辞書型を使って複数の引数を関数に渡すことができます。詳しくは後の章で辞書型を説明した後で紹介します。

位置引数なのかキーワード引数なのかを指定する

以下の様に / と * を使用して位置引数、キーワード引数のどちらを使用すべきかを明示的に定めることができます。
def function(pos1, pos2, /, pos_or_kwd, *, kwd1, kwd2):

  • / の前は位置引数を使用しなければなりません。(上記のケースは pos1, pos2)
  • / と * の間はどちらを使用しても構いません。(上記のケースは pos_or_kwd)
  • * の後ろはキーワード引数を使用しなければなりません。(上記のケースは kwd1, kwd2)

以下はまとめたものです。

# / も * もない場合は、位置引数、キーワード引数のどちらでも構いません。
def standard_arg(arg):
    print(arg)
    
# 最後に / がある場合は、位置引数のみ使用可能です。
def pos_only_arg(pos1, pos2, /):
    print(pos1, pos2)
    
# 最初に * がある場合は、キーワード引数のみ使用可能です。
def kwd_only_arg(*, kwd1, kwd2):
    print(kwd1, kwd2)
    
# / と * の両方ある場合は、/の前は位置引数専用、
# / と * の間は両方使用可能、*の後はキーワード引数専用です。
def combined_example(pos, /, arg, *, kwd):
    print(pos, arg, kwd)

以下は確認用コードです。コメントアウトした行はTypeErrorとなります。コメントアウトを解除してエラーを確認してみてください。

standard_arg('Hello')
standard_arg(arg = 'Hello')
pos_only_arg('Hello', 'World')
# pos_only_arg(pos1 = 'Hello', pos2 = 'World')
# kwd_only_arg('Hello', 'World')
kwd_only_arg(kwd1 = 'Hello', kwd2 = 'World')
# combined_example('Feel,', "don't", 'think.')
combined_example('Feel,', "don't", kwd = 'think.')
combined_example('Feel,', arg = "don't", kwd = 'think.')
# combined_example(pos = 'Feel,', arg = "don't", kwd = 'think.')

Hello
Hello
Hello World
Hello World
Feel, don’t think.
Feel, don’t think.

ドキュメンテーション文字列(docstring)を入れる

def文では、処理文の最初に記載された文字列(str)はdocstringとして認識され、実行時には無視されますが関数の属性として保存されます。通常はここに関数の解説を記載します。
以下のように三連の一重引用符(”’)や三連の二重引用符(“””)で囲ったものは、改行を含めて一つの文字列(str)として扱われます。docstringにはこのうち三連の二重引用符(“””)を使用することが推奨されています。

def price_incl_tax(price_without_tax, tax_rate = 10):
    """Return price including tax
    
    Parameters    price_without_tax: int or float
                     tax_rate: int (percentage)
    
    Return       price including tax: int  
    """
    result = price_without_tax * (1 + tax_rate/100) 
    return round(result)

上記の様にdocstringが入っていると、jupyter labでは以下の様に関数名を入力した後にshift+tabと打つことによりこのdocstringを含めた関数の情報を表示することができます。

docstringの推奨される書き方はPEP257に定められています。関数を作成した後、他の人が使用する可能性がある場合は、このPEP257に沿ってdocstringを記載することをお奨めします。

練習問題_2_11(練習問題_2_5の関数化)

前の章(if~elif~else文)の練習問題_2_5 のご自分の解答を使って、pressure_clsという関数を作成して下さい。
引数は、収縮期血圧(最高血圧)のmax_pressure および拡張期血圧(最低血圧)のmin_pressure
戻り値は、以下の図中の分類名(正常血圧、正常高値血圧など)です。
docstringも入れて下さい。また引数を変えて6種類の分類を出力する確認用コードも作成して下さい。

参照元:https://www.kyoukaikenpo.or.jp/g4/cat410/sb4020/r101/

ヒントはこちら
解答例はこちら

練習問題_2_12(練習問題_2_8 の関数化)

以下の内容の関数(seireki_to_gengo)を作成してください。
西暦(seireki)を入力する元号に変換する関数

  • 西暦(seireki)は 1926 以上 2024 以下の整数であることを判定し、それ以外の時は「 1926 以上 2024 以下の整数を入力してください」と表示
  • 1926 -> 大正15年/昭和元年
  • 1927 ~ 1988 -> 昭和2年 ~ 昭和63年
  • 1989 -> 昭和64年/平成元年
  • 1990 ~ 2018 -> 平成2年 ~ 平成30年
  • 2019 -> 平成31年/令和元年
  • 2020 ~ 2024 -> 令和2年 ~ 令和6年

以下の内容を含む Docstringを入れてください。

  • 西暦を元号に変換する関数であること
  • 引数は 1926 以上 2024 以下の整数
  • 戻り値はstrであること

ヒントはこちら
解答例はこちら

round関数について

ここで次の練習問題_2_13への導入のため、round関数について考えてみます。
これまでfloat型の小数以下の桁数を丸めるために round関数を使用してきました。またround関数を使用するに当たり「丸める」と言う表現を使用し、「四捨五入」と言う表現を避けてきました。
その理由を探るため、実際にround関数がどの様に処理しているのかを調べてみましょう。

round(0.499), round(0.500), round(0.501)

(0, 0, 1)

以上から、四捨五入とは違い5が切り捨てられる様に見えます。
ところが以下の様にいろいろ調べてみると5が切り上げになる場合もある様です。公式サイトはこちら

round(0.5), round(1.5), round(2.5), round(3.5)

(0, 2, 2, 4)

round(0.005, 2), round(0.015, 2), round(0.025, 2), round(0.035, 2)

(0.01, 0.01, 0.03, 0.04)

roundを使用する際にはこの点を頭の片隅に置く必要があるかと思います。

練習問題_2_13(四捨五入の関数)

それでは以下のような四捨五入の関数を作って下さい。
また、全ての条件について具体的な引数を使用して確認して下さい。(特に5の処理については詳しく)

  • 関数名:round2
  • 引数:roundと同じ、number(入力する数値)とndigits(小数点以下の桁数)
  • ndigitsがプラスの場合は、四捨五入して小数点ndigits桁の値を戻す
    • 例) round2(1.234, 1) -> 1.2, round2(9.876, 2) -> 9.88
  • ndigitsがマイナスの場合は、10x(ndigitsの絶対値-1)乗の位を四捨五入する
    • 例) round2(1234, -1) -> 1230, round2(9876, -2) -> 9900
  • ndigitsのデフォルト値:0
  • 戻り値の型:ndigitsが0またはマイナスの時はint型、ndigitsがプラスの時はfloat型
  • numberが数値(float型 or int型)ではない場合、またndigitsがint型でない場合は、「numberには数値、ndigitsには整数を入力して下さい」と出力
  • 日本語でも良いのでdocstringを入れて下さい。

ヒントはこちら
解答例はこちら

タイトルとURLをコピーしました