def(関数定義)
『Python』では『def』キーワードで関数を定義します。引数にはデフォルト値を設定でき、呼び出し側はキーワード引数で順序を自由に指定できます。Python 3.8 以降は位置専用引数(『/』)とキーワード専用引数(『*』)のセパレータを使って引数の渡し方を制限することも可能です。returnを省略するとNoneが返るのがデフォルト動作です。
構文
# 基本構文
def 関数名(引数1, 引数2):
# 処理
return 戻り値
# デフォルト値付き引数
def 関数名(引数1, 引数2=デフォルト値):
return 戻り値
# 位置専用引数(/)とキーワード専用引数(*)のセパレータ
# / より左は位置引数のみ、* より右はキーワード引数のみで渡す
def 関数名(位置専用, /, 通常, *, キーワード専用):
return 戻り値
# 可変長の位置引数(タプルで受け取る)
def 関数名(*args):
return args
# 可変長のキーワード引数(辞書で受け取る)
def 関数名(**kwargs):
return kwargs
キーワード・構文一覧
| 構文 | 概要 |
|---|---|
| def 関数名(引数): | 関数を定義します。インデントされたブロックが関数の本体になります。 |
| return 値 | 呼び出し元に値を返して関数を終了します。省略した場合はNoneが返ります。 |
| 引数=デフォルト値 | 引数を省略したときに使われるデフォルト値を指定します。デフォルト値のある引数は末尾にまとめます。 |
| 関数名(引数名=値) | 呼び出し時にキーワード引数として名前を指定して渡します。順序を問わず渡せます。 |
| def f(a, /, b, *, c): | /より左は位置引数のみ、*より右はキーワード引数のみで渡す必要があります。 |
| *args | 任意の数の位置引数をタプルとしてまとめて受け取ります。 |
| **kwargs | 任意の数のキーワード引数を辞書としてまとめて受け取ります。 |
サンプルコード
基本的な関数定義とreturnを確認します。returnを省略するとNoneが返ります。
basic_def.py
# Steins;Gate のキャラクターを紹介する関数を定義します。
def greet(name):
return "ようこそ、" + name + "!ラボメンへの加入を歓迎します。"
# 関数を呼び出して戻り値を受け取ります。
message = greet("牧瀬紅莉栖")
print(message)
# return を省略した関数は None を返します。
def log_entry(name):
print(name + "がラボに入室しました")
# return がないので None が返ります。
result = log_entry("岡部倫太郎")
print("戻り値:", result) # None が表示されます。
python3 basic_def.py ようこそ、牧瀬紅莉栖!ラボメンへの加入を歓迎します。 岡部倫太郎がラボに入室しました 戻り値: None
デフォルト値付き引数を使います。引数を省略した場合にデフォルト値が適用されます。
default_args.py
# ラボメンのステータスを表示する関数です。
# divergence はデフォルト値を持つ引数で、省略できます。
def show_status(name, divergence=0.000000):
print(name + " / 世界線変動率: " + str(divergence) + "%")
# divergence を省略するとデフォルト値の 0.000000 が使われます。
show_status("岡部倫太郎")
# 値を渡すとデフォルト値が上書きされます。
show_status("岡部倫太郎", 1.048596)
# 複数のデフォルト引数を持つ関数
def make_dmail(sender, receiver, body, is_critical=False):
prefix = "【重要】" if is_critical else ""
return prefix + sender + " → " + receiver + ": " + body
print(make_dmail("オカリン", "まゆり", "シュタインズゲートの選択"))
print(make_dmail("鳳凰院凶真", "バイト戦士", "明日の運命が変わる", True))
python3 default_args.py 岡部倫太郎 / 世界線変動率: 0.0% 岡部倫太郎 / 世界線変動率: 1.048596% オカリン → まゆり: シュタインズゲートの選択 【重要】鳳凰院凶真 → バイト戦士: 明日の運命が変わる
キーワード引数で引数名を指定して呼び出します。名前を指定することで順序に関係なく渡せます。
keyword_args.py
# Dメールの送信設定を行う関数です。
def send_dmail(sender, receiver, body, year, month, day):
print("[Dメール送信]")
print(" 差出人 :", sender)
print(" 宛先 :", receiver)
print(" 本文 :", body)
print(" 送信先 :", str(year) + "年" + str(month) + "月" + str(day) + "日")
# キーワード引数: 名前を指定するので順序を入れ替えても結果が同じです。
send_dmail(
receiver="過去の岡部倫太郎",
sender="岡部倫太郎",
body="秋葉原で待ってろ",
day=28,
month=7,
year=2010,
)
python3 keyword_args.py [Dメール送信] 差出人 : 岡部倫太郎 宛先 : 過去の岡部倫太郎 本文 : 秋葉原で待ってろ 送信先 : 2010年7月28日
位置専用引数(『/』)とキーワード専用引数(『*』)のセパレータを使います。
pos_kw_only.py
# / より左の引数は位置引数のみで渡さなければなりません。
# * より右の引数はキーワード引数のみで渡さなければなりません。
def register_labmem(name, /, lab_id, *, codename):
print("ラボメン登録: " + str(lab_id) + "号 " + name + "(" + codename + ")")
# name は位置引数のみ、codename はキーワード引数のみで渡します。
register_labmem("岡部倫太郎", 1, codename="鳳凰院凶真")
register_labmem("牧瀬紅莉栖", 2, codename="助手")
register_labmem("椎名まゆり", 3, codename="バイト戦士")
# 以下はエラーになります。
# register_labmem(name="岡部倫太郎", lab_id=1, codename="鳳凰院凶真")
# → name は / の左にあるので name= と書けません。
# register_labmem("橋田至", 4, "ダルの野望")
# → codename は * の右にあるので codename= と書かなければなりません。
python3 pos_kw_only.py ラボメン登録: 1号 岡部倫太郎(鳳凰院凶真) ラボメン登録: 2号 牧瀬紅莉栖(助手) ラボメン登録: 3号 椎名まゆり(バイト戦士)
関数はオブジェクトです。変数に代入したり、別の関数の引数として渡したりできます。
first_class.py
# 世界線の状態を評価する関数を定義します。
def is_beta(divergence):
return divergence >= 1.000000
def is_steins_gate(divergence):
return abs(divergence - 1.048596) < 0.000001
# 関数を変数に代入できます(関数オブジェクト)。
check = is_beta
print(check(1.048596)) # True
# 関数を引数として別の関数に渡せます(高階関数)。
def evaluate_world(divergence, checker):
if checker(divergence):
return "条件を満たす世界線です"
return "条件を満たしません"
print(evaluate_world(1.048596, is_steins_gate))
print(evaluate_world(0.571015, is_steins_gate))
# 関数をリストに格納して順番に呼び出せます。
checkers = [is_beta, is_steins_gate]
divergence = 1.048596
for fn in checkers:
print(fn.__name__ + ":", fn(divergence))
python3 first_class.py True 条件を満たす世界線です 条件を満たしません is_beta: True is_steins_gate: True
よくあるミス
よくあるミス1: ミュータブルなデフォルト値の罠
リストや辞書などのミュータブルなオブジェクトをデフォルト値に使うと、そのオブジェクトは関数定義時に一度だけ生成され、すべての呼び出しで共有されます。呼び出しのたびに新しいリストが作られません。
ng_mutable_default.py
# NG: デフォルト値のリストが全呼び出しで共有されます。
def add_member(name, members=[]):
members.append(name)
return members
print(add_member("岡部倫太郎"))
print(add_member("牧瀬紅莉栖")) # 前回の呼び出し結果が残っています。
print(add_member("椎名まゆり")) # さらに積み重なります。
python3 ng_mutable_default.py ['岡部倫太郎'] ['岡部倫太郎', '牧瀬紅莉栖'] ['岡部倫太郎', '牧瀬紅莉栖', '椎名まゆり']
ok_mutable_default.py
# OK: デフォルト値を None にして、関数内で初期化します。
def add_member(name, members=None):
if members is None:
members = []
members.append(name)
return members
print(add_member("岡部倫太郎"))
print(add_member("牧瀬紅莉栖")) # 毎回新しいリストが作られます。
print(add_member("椎名まゆり"))
python3 ok_mutable_default.py ['岡部倫太郎'] ['牧瀬紅莉栖'] ['椎名まゆり']
よくあるミス2: デフォルト値なし引数をデフォルト値あり引数の後に書くSyntaxError
デフォルト値なしの引数はデフォルト値あり引数より前に書く必要があります。後に書くとSyntaxErrorになります。
ng_arg_order.py
# NG: デフォルト値なし引数をデフォルト値あり引数の後に書いています。
def send_dmail(sender, body="(本文なし)", receiver):
print(sender, "→", receiver, ":", body)
python3 ng_arg_order.py
File "ng_arg_order.py", line 2
def send_dmail(sender, body="(本文なし)", receiver):
^^^^^^^^
SyntaxError: non-default argument follows default argument
ok_arg_order.py
# OK: デフォルト値なし引数を先に書きます。
def send_dmail(sender, receiver, body="(本文なし)"):
print(sender, "→", receiver, ":", body)
send_dmail("岡部倫太郎", "過去の岡部倫太郎")
send_dmail("鳳凰院凶真", "まゆり", "シュタインズゲートの選択")
python3 ok_arg_order.py 岡部倫太郎 → 過去の岡部倫太郎 : (本文なし) 鳳凰院凶真 → まゆり : シュタインズゲートの選択
よくあるミス3: 関数定義より前に呼び出すNameError
Pythonの『def』文は実行時に評価されます。関数が定義される前にその関数を呼び出すとNameErrorになります。
ng_call_before_def.py
# NG: 関数が定義される前に呼び出しています。
greet("牧瀬紅莉栖")
def greet(name):
print("ようこそ、" + name + "!ラボメンへの加入を歓迎します。")
python3 ng_call_before_def.py
Traceback (most recent call last):
File "ng_call_before_def.py", line 2, in <module>
greet("牧瀬紅莉栖")
^^^^^
NameError: name 'greet' is not defined
ok_call_before_def.py
# OK: 関数を定義してから呼び出します。
def greet(name):
print("ようこそ、" + name + "!ラボメンへの加入を歓迎します。")
greet("牧瀬紅莉栖")
greet("岡部倫太郎")
python3 ok_call_before_def.py ようこそ、牧瀬紅莉栖!ラボメンへの加入を歓迎します。 ようこそ、岡部倫太郎!ラボメンへの加入を歓迎します。
概要
Pythonの『def』文は実行時に評価される文(statement)です。関数定義は実行された時点で関数オブジェクトが生成され、関数名という変数に代入されます。このため関数を変数に代入したり、他の関数に引数として渡したりできます(第一級オブジェクト)。
デフォルト値は関数定義時に一度だけ評価されます。ミュータブルなオブジェクト(リスト・辞書など)をデフォルト値にすると、呼び出しをまたいで値が共有されてしまう罠があります。ミュータブルをデフォルト値にしたいときは『None』をデフォルトにして関数内で初期化するパターンが定石です。
位置専用引数(『/』)とキーワード専用引数(『*』)はPython 3.8以降で使えます。ライブラリのAPIを設計する際に「引数名を変更しても呼び出し側に影響しない」「重要な設定は必ずキーワード引数で明示させる」といった意図を伝えられます。無名関数として使うlambdaや、関数を受け取って機能を追加するデコレータも合わせて参照してください。
記事の間違いや著作権の侵害等ございましたらお手数ですがこちらまでご連絡頂ければ幸いです。