関数 基礎#
関数の基本的な構文は以下の通りです。
Info
戻り値がない関数では、return 文は省略してもかまいません。 その場合も関数は(何も返さないわけではく)None という値を返したと見なされます。
定義した関数は以下のように呼び出します。
関数名(引数1, 引数2, ...)
以下は簡単な関数の例です。
Tip
- 関数はその役割を名前で把握できるような命名を心がける。
create_topologuy
のように動詞_名詞
のような名前だと分かりやすい。- 1つの関数に1つの役割としたほうが何かと都合がよい。
ラムダ式#
- ラムダ式は名前のない関数です。
- 1文程度の処理で関数を定義するほどでもない、簡潔な処理を書く場面などに使用します。
変数 = lambda 引数, ... : 式または処理
の構文で記述します。map
・filter
・sort
など引数に関数をとる関数を呼び出す際に使用すると便利です。
以下は税込み料金の計算をlambdaを用いて記述した例です。
変数のスコープ#
スコープとはコードの中で変数を参照できる範囲の事を指します。 Pythonのスコープはコード全体から参照できるグローバルスコープと、関数の中でのみ参照できるローカルスコープがあります。なお、スコープをまたいで参照する場合は、ローカルスコープからグローバルスコープは参照可能ですが、その逆は基本的に不可です。
スコープ内の変数について、グローバルスコープの変数をグローバル変数、ローカルスコープの変数をローカル変数といいいます。グローバル変数の寿命については意識することはありませんが、ローカル変数の寿命(メモリでの保持期間)は、関数内での処理が終わるまでです。
グローバル変数#
- グローバル変数は関数の外で定義します。
- if 文と
globals()
メソッド を使用することで、その変数がグローバル変数に存在するか確認できます。
def order_sushi(sushi_name):
print(f"One {sushi_name} nigiri please!")
sushi = "salmon"
order_sushi(sushi)
# One Salmon nigiri please!
if "sushi" in globals():
print("sushi is global.")
# sushi is global.
変数を関数の外側に定義することによって関数内からでもアクセスできるようになります。
関数内でグローバル変数を定義するするには global 文を記述します。
def order_sushi():
global sushi
sushi = "Salmon"
print(f"One {sushi} nigiri please!")
if "sushi" in globals():
print("sushi is global.")
# sushi is blobal
Warning
- グローバル変数に影響を与える関数はコードの可読性を損なう。
- 特別な理由がない限り、関数の結果を返す用途には本来の戻り値を利用すること
- 同様に、引数で渡した変数を関数内で書き換えるのはよい考えではない
- 特にミュータブルな型(リストや辞書など)を引数として渡した際には意図しないところで値が書き換わってかよく確認すること。
ローカル変数#
- ローカル変数は関数の内側で定義します。
- if 文と
locals() メソッド
を使用することで、その変数がローカル変数に存在するか確認できます。 - グローバルスコープからローカル変数は参照できません。
def order_sushi(sushi_name):
sushi = "salmon" # local
print(f"One {sushi_name} nigiri please!")
if "sushi" in locals():
print("sushi is local.")
# sushi is local.
print(sushi)
# NameError: name 'sushi' is not defined
ネストされた関数#
関数の中に関数を定義する、いわゆる入れ子で(=ネストされた構造で)定義することができます。 変数を探す際には、現在のスコープを基点に、存在しない場合は順位上位のスコープに遡って探索する動作となります(スコープチェーンといいます)。ここでも上位のスコープから下位のスコープを参照することはできません。
nonlocal 文#
nonlocal 文は一つ外側のスコープで先に定義された変数を参照するようにします。
以下の例では、nonlocal 文により、var 変数は inner 関数内で outer スコープの var = 'outer'
を
参照します。
var = 'global'
def outer():
var = 'outer'
def inner():
nonlocal var
# var = 'inner'
print(f"inner scope: {var}")
return var
# outer のスコープから innter 関数の呼び出し
inner()
print(f"outer scope: {var}") # inner
# global スコープから outer 関数の呼び出し
outer()
print(f"global scope: {var}") # global
# 結果:
# inner scope: outer <---★
# outer scope: outer
# global scope: global
nonlocal 文を削除し、var = 'inner'
と記述した場合の結果は以下のとおりです。
var = 'global'
def outer():
var = 'outer'
def inner():
var = 'inner'
print(f"inner scope: {var}")
return var
# outer のスコープから innter 関数の呼び出し
inner()
print(f"outer scope: {var}") # inner
# global スコープから outer 関数の呼び出し
outer()
print(f"global scope: {var}") # global
# 結果:
# inner scope: inner <---★
# outer scope: outer
# global scope: global
引数#
仮引数と実引数#
- 引数とは、関数の中で参照可能な変数のことです。
- 関数を呼び出す際、呼び出し側から関数に値を引き渡すために利用されます。
- 一般的に、呼び出し元から渡される値のことを実引数、受け取り側の変数のことを仮引数といいます。
入門 Python 3 第2版 で分かりやすく説明してあったので以下引用します。
関数を呼び出すときに関数に渡す値を実引数という。実引数を渡して関数を呼び出すとき、 それらの値は関数内の対応する仮引数にコピーされる。 関数の外からは実引数、関数の中では仮引数という言い方もできる。英語では、 実引数がargument、仮引数がparameterとまったく違う単語なので、両者は否応なく、 はっきり区別されるが、日本語では「実」「仮」を省略して引数と呼び、両者を区別しないことも多い。
位置引数#
関数定義時に順番に記述されている仮引数のことです。
def morning_menu(food, drink, dessert):
print(f"朝食のメインは {} です。")
print(f"飲み物は {} です。")
print(f"デザートは {} です。")
morning_menu("eggs Benedict", "Coffee", "Pudding")
キーワード引数#
キーワード引数は関数呼び出し時に、引数=値
のように、明示的に引数の名前を指定できる引数のことです。
詳しくは以下のデフォルト引数を参照してください。
デフォルト引数#
関数を定義するタイミングで仮引数に既定値を設定しておくことです。 関数呼び出し時に、実引数を渡さなかった場合、あらかじめ定義した仮引数が呼び出されます。
def base_price(tuna=100,salmon=200,shrimp=300):
print(f"tuna: {tuna} 円")
print(f"salmon: {salmon} 円")
print(f"shrimp: {shrimp} 円")
base_price()
# tuna: 100 円
# salmon: 200 円
# shrimp: 300 円
Warning
デフォルト引数が評価されるのは、関数定義時の一度だけである点に注意です。 意図した結果にならない場合には、どのタイミングで関数を定義し、いつその関数を 呼び出しているか確認します。
Tip
空のリストを引数として宣言すると、空のリストは一度のみしか評価されません。 複数回関数を呼び出すと値が累積していきます。
以下のように書き換えると実行時に都度、既定値を生成することができます。
位置引数とキーワード引数の混在#
位置引数とキーワード引数を同時に宣言する際には、位置引数、キーワード引数の順に宣言する決まりです。
位置引数 / キーワード引数の強制#
仮引数に /
を入れると *
の前の引数を位置引数としてのみ扱うことを強制できます。
*
以降はキーワード引数として渡さなければならない点に注意です。
python3.8 以降では /
を宣言することで、*
と同様に、それ以前は位置引数として強制できるようになりました。
Note
/
のあとにも位置引数の記述は可能です。
可変長引数#
関数定義にて 引数に*
と**
をつけると、任意の数の引数(可変長引数)を参照できます。
*
と**
に続く文字列は何でもいいですが、慣例的に、args
、kwargs
が用いられます。
*args#
*args
は複数の引数をタプルとして受け取ります。
def calc_pay(*args):
total = sum(args)
return total
price = calc_pay(100,200,300)
print(price)
# 600
**kwargs#
**kwargs
は複数のキーワード引数を辞書として受け取ります。
def create_sushiMenu(**kwargs):
print("shshi Menu: ", kwargs)
create_sushiMenu(tuna=100, mackerel=150, salmon=200,)
# shshi Menu: {'tuna': 100, 'mackerel': 150, 'salmon': 200}
可変長の実引数#
先の例では仮引数にて可変長引数を宣言していましたが、その逆も可能です。
以下の例では *
を用いてアンパックしたリストを実引数で渡しています。
def calc_pay(arg1, arg2, arg3):
total = arg1 + arg2 + arg3
return total
price = [100,200,300]
print(calc_pay(*price))
# 600
以下の例では **
を用いてアンパックした辞書を実引数で渡しています。
def create_sushiMenu(tuna=100, mackerel=150, salmon=200):
print(f"tuna: {tuna}")
print(f"mackerel: {mackerel}")
print(f"salmon: {salmon}")
menu = {"tuna": 100, "mackerel":150, "salmon":200}
create_sushiMenu(**menu)
戻り値#
関数の戻り値はretrun
にて受け取ることができます。
複数の戻り値#
戻り値が複数ある場合、タプルとして返すのが一般的です。