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 を指定し、その後にキーワードで brightnessvolume を指定しています。

悪い例(キーワード引数の後に、位置引数)

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個):複数の「キーワード引数」を辞書としてまとめて受け取る。

これらを含めた場合、関数の引数には、絶対に守らなければならない「究極の順番」が存在します。

それが、この順番です!

  1. 普通の引数(デフォルト値なしの位置引数)
  2. デフォルト引数
  3. *args (可変長・位置引数)
  4. キーワード専用引数* の後に書く引数、または *args の後に書く引数)
  5. **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'}

何が起こったか見てみましょう。

  • 12 は、(1)の ab に入りました。
  • 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年以上。
すべての無駄を省いた費用対効果の高い「筋肉質」な研修を提供します!
この記事に間違い等ありましたらぜひお知らせください。