🐍

【*】Python関数引数でのアスタリスクの使い方まとめ【**】

に公開

はじめに

Pythonの関数の引数で***が付くものを見たことはありますでしょうか?
私はありますが意味がよくわからないので見て見ぬふりをしてきました。

最近また見る機会があり「そろそろ意味や使い方を理解しよう」と思ったので、調べて学んだ内容を共有したいと思います。

***はどんなときに使う?

関数の引数で***を使うのは、主に 「同じ処理を使いまわしたいけど、処理によって引数が少しずつ違う」 ときです。

***を使うことで引数の数や引数名を固定しない関数を定義することができます。

以下、具体的な使い方を紹介していきます。

*を1つつけた引数(*args):複数の引数をタプルにまとめて受け取る

関数に渡される複数の引数をタプルにまとめて受け取るときに使います。
関数を呼び出すときに引数の数が変わってもOKです。

def greet(*args):
    for name in args:
        print(f"Hello, {name}!")

greet("Alice") # 引数1つ
# Hello, Alice!

greet("Alice", "Bob", "Charlie") # 引数3つ
# Hello, Alice!
# Hello, Bob!
# Hello, Charlie!

慣例的にargsという引数名を使いますが、実際には好きな引数名が使えます(*がついていればOK)。

引数の数が決まっていないときに使うと便利です。
たとえばログ出力・合計計算・まとめ処理などでよく使います。

注意点として*args以降に定義された引数は、必ずキーワード指定で渡す必要があります。

# *args を使う場合
def sample(*args, x, y):
    print(args, x, y)

sample(1, 2, 3, x=4, y=5)  # ✅ OK
# sample(1, 2, 3, 4, 5)   # ❌ NG:x, y はキーワード指定が必要

*を2つつけた引数(**kwargs):キーワード引数を辞書にまとめて受け取る

こちらは、キーワード付きの引数をまとめて辞書として受け取るものです。

def show_info(**kwargs):
    for key, value in kwargs.items():
        print(f"{key}: {value}")

show_info(name="Alice", age=25, country="Japan")
# name: Alice
# age: 25
# country: Japan

慣例的にkwargs(keyword argumentsの略)という引数名を使いますが、こちらも好きな引数名が使えます。

設定値やオプションをまとめて扱いたいときに便利です。
たとえば「将来的に引数が増える」関数にも柔軟に対応できます。

注意点として**kwargsは関数内の最後の引数とする必要があり、これ以降に別の引数を定義するとエラーになります。

# ✅ OK
def sample(a, b, **kwargs):
    print(a, b, kwargs)

# ❌ NG
# def sample(a, b, **kwargs, c):
#     print(a, b, kwargs, c)

*だけの引数

* だけの引数を定義すると、それ以降の引数はキーワード指定必須の引数になります。

def move(x, y, *, speed=1.0, direction="forward"):
    ...

# ✅ OK
move(10, 20, speed=2.0, direction="backward") 

# ❌ NG:speed, direction はキーワード指定が必要
# move(10, 20, 2.0, "backward") 

メインの引数(位置)とオプションの引数(キーワード)を明確に分けたいときなどに使います。

* / ** を使って引数を「展開」する

引数を定義するときだけでなく、関数に引数を渡すときに*を使うこともあります。

具体的には、リストや辞書にまとめたデータを分解して関数に渡すことができます。
これを アンパック(引数展開) と呼びます。

例1:リストやタプルを引数として展開する

def add(a, b, c):
    return a + b + c

numbers = [1, 2, 3]
result = add(*numbers)  # add(1, 2, 3) と同じ
print(result)  # 出力: 6

*numbers とすることで、リストの中身を位置引数として展開して渡すことができます。

例2:辞書をキーワード引数として展開する

def show_profile(name, age, country):
    print(f"{name} ({age}) from {country}")

profile = {"name": "Alice", "age": 25, "country": "Japan"}
show_profile(**profile)
# show_profile(name="Alice", age=25, country="Japan") と同じ

**profile は、辞書のキーを引数名として展開して渡します。

実践例:共通処理をまとめる関数で使ってみる

「関数ごとに引数が違うけど、処理の流れは同じ」というケースに *args**kwargs が活躍します。
具体例として、異なる引数を持つ関数の前後で共通処理を行う例を紹介します。

# 引数として渡された関数の前後で共通の処理を行う
def process_anything(func, *args, **kwargs):
    print("=== Start process ===")
    # 渡された関数funcを、引数(*args, **kwargs)付きで実行
    result = func(*args, **kwargs)
    print("=== End process ===")
    return result


def process_user(user_id, *, active=True, notify=False):
    print("Processing user:", user_id, active, notify)


def process_order(order_id, *, priority=False):
    print("Processing order:", order_id, priority)


# process_user関数を共通処理の関数に渡して実行
process_anything(process_user, 123, notify=True)
# === Start process ===
# Processing user: 123 True True
# === End process ===

# process_order関数も同じ関数で実行(関数の種類が違ってもOK)
process_anything(process_order, 999, priority=True)
# === Start process ===
# Processing order: 999 True
# === End process ===

ポイント

  • process_anything() は、引数の数やキーワードが違っても呼べる
  • 共通処理を一つの関数にまとめられる
  • ログ、例外処理、DB接続などに使うと便利

* / ** を使うべきシチュエーションはこんなとき

シチュエーション 使う記法 メリット
引数の数が可変 *args 柔軟に対応できる
オプション引数をまとめたい **kwargs 拡張しやすい
キーワード指定を強制したい def func(*, option=...) 安全・明示的
リスト・辞書を展開して渡したい *obj / **obj シンプルに書ける
共通処理で関数を包みたい *args, **kwargs 再利用性アップ

まとめ

  • *つきの引数(*args):位置引数をまとめる
  • **つきの引数(**kwargs):キーワード引数をまとめる
  • *だけの引数:キーワード指定を強制する
  • * / ** をつけて関数に渡す:データを展開して渡す
  • 「処理の流れは同じだけど、引数が少し違う」ときに使うと◎

おわりに

難しそうなので今まであまり触れてきませんでしたが、調べてみるととても便利な機能だとわかりました。
「あの時のコードに使えばよかったな」と思いあたる節が多々あるので、もっと早く知りたかったです。
今後は積極的に活用していきたいと思います。

Discussion