Python関数の引数の罠!正しい順番をマスターしてエラーを回避しよう
こんにちは。ゆうせいです。
Pythonで関数を自作(定義)するとき、とても便利ですよね。
def my_function(name): のように、シンプルなものなら迷うことはありません。
しかし、引数が2つ、3つと増えてきたり、オプションの引数を設定したりし始めると、とたんに「あれ、どういう順番で書けばいいんだっけ?」と悩む瞬間が訪れます。
特に、SyntaxError: non-default argument follows default argument のようなエラーメッセージに出会ったことはありませんか?
これは、Pythonが定めた引数の「厳格な順番ルール」を破ってしまったときに起こるエラーなんです。
今日は、この引数の順番に関する「鉄の掟」を、ゼロからしっかり学んでいきましょう!
これをマスターすれば、もうエラーを恐れる必要はありませんよ。
引数の基本:位置引数 (Positional Arguments)
まず、一番シンプルな引数からおさらいです。
「位置引数」とは、その名の通り「書かれた位置(順番)」によって、どの引数に値が渡されるかが決まるものです。
Python
def profile(name, age):
print(f"名前: {name}さん, 年齢: {age}才")
# 1番目がname, 2番目がageに渡される
profile("山田", 30)
実行結果:
名前: 山田さん, 年齢: 30才
もし、この順番を間違えてしまうと…
Python
# 1番目がname, 2番目がageに渡される
profile(30, "山田")
実行結果:
名前: 30さん, 年齢: 山田才
大変なことになってしまいました!
これが「位置」が重要である理由です。
順番を気にしない:キーワード引数 (Keyword Arguments)
「いちいち順番を覚えるのが面倒だ!」というときに便利なのが「キーワード引数」です。
これは、引数名を直接指定して値を渡す方法です。
Python
def profile(name, age):
print(f"名前: {name}さん, 年齢: {age}才")
# キーワード(引数名)を指定すれば、順番は自由
profile(age=25, name="佐藤")
実行結果:
名前: 佐藤さん, 年齢: 25才
これなら、関数を定義したときの引数の順番(name の次が age)を忘れていても、age=... と明記することで、Pythonが正しく値を割り当ててくれます。安全で読みやすい方法ですね。
ルール1:位置引数は、キーワード引数より「前」!
さて、ここからが本題のルールです。
「位置引数」と「キーワード引数」を混ぜて使うことはできるのでしょうか?
できます!ただし、厳格なルールがあります。
必ず、「位置引数」をすべて書き終えてから、「キーワード引数」を書き始めてください。
例えるなら、お店のレジ待ちです。
「位置引数」は、単純に列に並んでいる人たち。
「キーワード引数」は、「予約していた鈴木です!」と名前を名乗る人。
レジ係(Python)は、まず列に並んでいる人たち(位置引数)を順番にさばきます。
その人たちが全員終わった 後で、「お名前でご予約の方どうぞ(キーワード引数)」と対応します。
もし、名前を名乗る人(キーワード引数)が割り込んだ 後で、再び列に並ぶ人(位置引数)が来たら、レジ係は混乱してしまいますよね?
良い例(位置引数 → キーワード引数)
Python
def settings(mode, volume, brightness):
print(f"モード: {mode}, 音量: {volume}, 明るさ: {brightness}")
# 1番目のmodeだけ位置で指定し、残りはキーワードで指定
settings("Movie", brightness=70, volume=50)
実行結果:
モード: Movie, 音量: 50, 明るさ: 70
これはOKです。まず位置で mode を指定し、その後にキーワードで brightness と volume を指定しています。
悪い例(キーワード引数の後に、位置引数)
Python
# 悪い例:これを実行するとエラーになります
settings(mode="Movie", 80, brightness=70)
これを実行すると SyntaxError: positional argument follows keyword argument というエラーが出ます。
「キーワード引数(mode="Movie")を書いたのに、その後に位置引数(80)が来たぞ!」とPythonが怒っているわけです。
ルール2:デフォルト引数は、普通の引数より「後」!
関数を定義するとき、「この引数は、指定されなかったら『初期値』を使ってほしいな」という場合があります。
これが「デフォルト引数」です。
Python
# messageがデフォルト引数
def greet(name, message="こんにちは"):
print(f"{name}さん、{message}")
# messageを省略しても、デフォルト値が使われる
greet("高橋")
# もちろん指定もできる
greet("伊藤", message="こんばんは")
実行結果:
高橋さん、こんにちは
伊藤さん、こんばんは
便利ですよね。
しかし、このデフォルト引数を定義する場所にも、厳格なルールがあります。
必ず、「デフォルト値を持たない普通の引数(位置引数)」をすべて書き終えてから、「デフォルト引数」を書き始めてください。
良い例(普通の引数 → デフォルト引数)
Python
def buy(price, item="りんご"): # OK
print(f"{item}を{price}円で買う")
悪い例(デフォルト引数の後に、普通の引数)
Python
# 悪い例:これを実行しようとするとエラーになります
def buy(item="りんご", price): # NG
print(f"{item}を{price}円で買う")
これを書いた時点で SyntaxError: non-default argument follows default argument のエラーが出ます。
「デフォルト値がある引数(item="りんご")を書いたのに、その後にデフォルト値がない引数(price)が来たぞ!」というエラーです。
考えてみれば当然で、もしこの悪い例が許された場合、
buy(100) と呼び出したときに、Pythonは 100 を item に入れるべきか price に入れるべきか判断できませんよね?
だから、必須の引数(price)を先に書くルールになっているのです。
最終形態:究極の順番(*argsと**kwargsを含む)
さて、いよいよ大詰めです。
Pythonには、「引数がいくつ来るかわからない」ときに備えて、それらをまとめて受け取る特殊な引数があります。
*args(アスタリスク1個):複数の「位置引数」をタプル(リストの仲間)としてまとめて受け取る。**kwargs(アスタリスク2個):複数の「キーワード引数」を辞書としてまとめて受け取る。
これらを含めた場合、関数の引数には、絶対に守らなければならない「究極の順番」が存在します。
それが、この順番です!
- 普通の引数(デフォルト値なしの位置引数)
- デフォルト引数
*args(可変長・位置引数)- キーワード専用引数(
*の後に書く引数、または*argsの後に書く引数) **kwargs(可変長・キーワード引数)
この5つをすべて使った関数定義は、以下のようになります。
# (1) (2) (3) (4) (5)
def full_func(a, b, c=10, *args, option=True, **kwargs):
print(f"--- 関数の実行結果 ---")
print(f"(1) 普通の引数 a: {a}")
print(f"(1) 普通の引数 b: {b}")
print(f"(2) デフォルト引数 c: {c}")
print(f"(3) *args: {args}")
print(f"(4) キーワード専用引数 option: {option}")
print(f"(5) **kwargs: {kwargs}")
# この関数を呼び出してみましょう
full_func(1, 2, 3, "追加1", "追加2", option=False, user="Yusei", status="OK")
実行結果:
--- 関数の実行結果 ---
(1) 普通の引数 a: 1
(1) 普通の引数 b: 2
(2) デフォルト引数 c: 3
(3) *args: ('追加1', '追加2')
(4) キーワード専用引数 option: False
(5) **kwargs: {'user': 'Yusei', 'status': 'OK'}
何が起こったか見てみましょう。
1と2は、(1)のaとbに入りました。3は、(2)のcに入りました(デフォルトの10ではなく3が使われました)。"追加1"と"追加2"は、位置で指定されましたが、もう入る場所が(1)(2)にありません。そこで、(3)の*argsが「残りの位置引数」としてすべてキャッチし、タプル('追加1', '追加2')になりました。option=Falseは、(4)のoptionに入りました。*argsの後ろにある引数は、必ずキーワード(option=...の形)で指定しなくてはなりません。これが「キーワード専用」と呼ばれる理由です。user="Yusei"とstatus="OK"は、定義されていないキーワード引数です。そこで、(5)の**kwargsが「残りのキーワード引数」としてすべてキャッチし、辞書{'user': 'Yusei', 'status': 'OK'}になりました。
まとめと今後の学習
お疲れ様でした!なんだかルールが多くて大変でしたか?
でも、安心してください。普段よく使うのは (1) と (2) だけです。
関数の引数を定義するときは、「必須の引数」を先に書き、その後に「オプションの引数(デフォルト値あり)」を書く!
まずは、このルール(ルール2)を徹底するだけで、SyntaxError に悩まされることは劇的に減るはずです。
*args や **kwargs は、自分で定義するよりも、他の人が作ったライブラリ(高機能なツール群)を使うときによく見かけます。
そのときに、「ああ、これは引数をまとめて受け取ってるんだな」と思い出せれば完璧です。
今後の学習としては、引数の「型ヒント(Type Hinting)」を学んでみることをお勧めします。
これは引数の「順番」とは関係ありませんが、「この引数には数値(int)を入れてください」「こっちには文字列(str)を」と、あらかじめ指定できる機能です。
型ヒントを使うと、コードがさらに読みやすく、バグが起きにくくなりますよ!
セイ・コンサルティング・グループの新人エンジニア研修のメニューへのリンク
投稿者プロフィール
- 代表取締役
-
セイ・コンサルティング・グループ株式会社代表取締役。
岐阜県出身。
2000年創業、2004年会社設立。
IT企業向け人材育成研修歴業界歴20年以上。
すべての無駄を省いた費用対効果の高い「筋肉質」な研修を提供します!
この記事に間違い等ありましたらぜひお知らせください。
最新の投稿
山崎講師2025年10月26日Pythonのimport完全ガイド!パッケージとモジュールの階層構造をスッキリ理解しよう
山崎講師2025年10月26日Python関数の引数の罠!正しい順番をマスターしてエラーを回避しよう
山崎講師2025年10月26日Pythonの「浅いコピー」と「深いコピー」の違いとは?コピーの罠を徹底解説!
山崎講師2025年10月26日VSCodeを自分好みに育てる!新人エンジニアのためのsettings.json徹底ガイド