順番に読み進めながら学べます

関数の可変長引数 *args / **kwargs と複数の戻り値

Python の可変長引数 args / *kwargs と複数の戻り値を図解で解説します。アンパックを含めた基本パターンまで一通り押さえます。

前回def では、引数の数をあらかじめ決めて関数を定義しました。実際の処理では、いくつ引数が渡されるか事前には決まらない場合や、1 回の呼び出しで複数の結果を返したい場合が出てきます。

これらはそれぞれ 可変長引数複数の戻り値 という仕組みで表現できます。どちらもタプルと辞書の知識が必要です。

可変長の位置引数 *args — タプルでまとめて受け取る

引数名の前にアスタリスクを 1 つ付けて *args とすると、位置引数を何個でもまとめて受け取れるようになります。関数の中では、受け取った引数はタプルとして扱えます。

名前は慣習で args がよく使われますが、技術的には *prices のように用途が読める名前でよいです。

*args で位置引数をまとめて受け取る
呼び出し側sum_all(100, 200, 300)関数定義def sum_all(*prices):関数の中でprices = (100, 200, 300)渡すまとめる
# いくつでも位置引数を受け取れる
def sum_all(*prices):
    print(prices)        # (100, 200, 300)
    print(type(prices))  # <class 'tuple'>
    return sum(prices)

print(sum_all(100, 200, 300))   # 600
print(sum_all(500))             # 500(1 個でも OK)
print(sum_all())                # 0(0 個なら空タプル)

通常の引数と*argsを組み合わせる

*args の前に通常の引数を置けば、先頭の決まった引数後ろの可変長部分を一緒に扱えます。log(level, *messages) のように書くと、最初の値は level に、残りはすべて messages というタプルにまとめられます。順序は通常の引数 → *args の順で書きます。

通常の引数と*argsの組み合わせ
log("INFO", "起動しました", "接続成功")def log(level, *messages):level = "INFO"messages =("起動しました", "接続成功")渡す先頭は通常引数残りはタプル
def log(level, *messages):
    for m in messages:
        print(f"[{level}] {m}")

log("INFO", "起動しました", "接続成功")
# [INFO] 起動しました
# [INFO] 接続成功

商品価格をいくつでも受け取って合計を返す関数を作ります。

def calc_total(*prices): で関数を定義し、sum(prices)return してください。

calc_total(300, 500)calc_total(120, 280, 550, 400) の結果をそれぞれ print() で表示してください。個数が違っても同じ関数で処理できることを確認しましょう。

(正しく実行できれば解説が表示されます)

Python エディタ

コードを実行してください

可変長のキーワード引数 **kwargs — 辞書でまとめて受け取る

アスタリスクを 2 つ付けて **kwargs とすると、**名前=値` の形で渡されたキーワード引数をまとめて受け取れます。関数の中では辞書として扱えるので、キー名が事前に決まっていない値**の受け渡しに使えます。

名前は慣習で kwargs(keyword arguments の略)がよく使われます。

**kwargsでキーワード引数を辞書で受け取る
呼び出し側show(name="田中", age=30)関数定義def show(**info):関数の中でinfo = {"name": "田中", "age": 30}型: dict渡すまとめる

show(name="田中", age=30) のように渡すと、受け取る側では {"name": "田中", "age": 30} という辞書になります。キー名を事前に決めずに受け取れるのが特徴です。

def describe_user(**info):
    print(info)            # {'name': '田中', 'age': 30, 'city': '東京'}
    print(type(info))      # <class 'dict'>
    for key, value in info.items():
        print(f"{key}: {value}")

describe_user(name="田中", age=30, city="東京")

通常の引数と **kwargs を組み合わせる

通常の引数のあとに **kwargs を置けば、**必須の値はキーワードを固定**しつつ、**それ以外の任意項目は柔軟に受け取れる**書き方になります。def save_user(name, **info): のように書くと、name だけは必ず受け取り、追加の age=30city="東京" などはすべて info という辞書にまとめられます。

通常の引数と**kwargsの組み合わせ
save_user("田中", age=30, city="東京")def save_user(name, **info):name = "田中"info ={"age": 30, "city": "東京"}渡す先頭は通常引数残りは辞書
def save_user(name, **info):
    print(f"name: {name}")
    for key, value in info.items():
        print(f"{key}: {value}")

save_user("田中", age=30, city="東京")
# name: 田中
# age: 30
# city: 東京

ユーザー情報をキーワード引数で受け取り、1 行ずつ表示する関数を作ります。

def show_profile(**info): を定義し、for key, value in info.items(): で回して print(f"{key}: {value}") を出力してください。

show_profile(name="太郎", age=20, city="大阪") を呼び出してください。

Python エディタ

コードを実行してください

通常引数, *args, **kwargsを同時に使う

3 種類の引数は同時に書けます。順序は 通常の引数 → *args → **kwargs で固定です。def register(user_id, *tags, **meta): のように書くと、最初の値は user_id、それ以降の位置引数はすべて tags というタプル、key=value 形式の引数はすべて meta という辞書に分かれて入ります。

def register(user_id, *tags, **meta):
    print(f"id: {user_id}")
    print(f"tags: {tags}")
    print(f"meta: {meta}")

register(1001, "admin", "beta", email="taro@example.com", active=True)
# id: 1001
# tags: ('admin', 'beta')
# meta: {'email': 'taro@example.com', 'active': True}

通常引数・*args**kwargs を同時に使う関数を作ります。

def register(user_id, *tags, **meta): を定義し、print(f"id: {user_id}")print(f"tags: {tags}")print(f"meta: {meta}") の 3 行を出力してください。

register(1001, "admin", "beta", email="taro@example.com", active=True) で呼び出して、それぞれの引数がどこに分かれて入るかを確認しましょう。

Python エディタ

コードを実行してください

呼び出し側のアンパック — *リスト / **辞書

すでに作られたリストや辞書を関数に渡すときは、* や `付けるかどうか**で結果が大きく変わります。

*** を付けずにそのまま渡すと、リストや辞書は**展開されないまま 1 個の値**として扱われます。*args で受けると、中身がバラけずに「リストが 1 個入ったタプル」(例: ([1, 2, 3],)`)になってしまうのがポイントです。

呼び出し側で `リスト / 辞書 と書くと、リストや辞書の中身が 1 つずつバラけて渡るので、*args / **kwargs 側では期待どおり (1, 2, 3) というタプルや {"name": "花子"} という辞書を受け取れます。

* を付けるかどうかで *args の中身が変わる
lst = [1, 2, 3]f(lst)(* なし)args =([1, 2, 3],)(リスト 1 個)lst = [1, 2, 3]f(*lst)(* あり)args =(1, 2, 3)(中身がバラけた)そのまま 1 個中身を展開
def sum_all(*prices):
    return sum(prices)

prices = [120, 280, 550]

# × リスト 1 個として渡される
# print(sum_all(prices))     # エラー(sum が中身とリスト自体を足そうとする)

# ○ `*` でバラす
print(sum_all(*prices))       # 950 — sum_all(120, 280, 550) と等価

# 辞書も同じ
def announce(name, city):
    print(f"{name} さん({city})")

user = {"name": "花子", "city": "札幌"}
announce(**user)              # announce(name="花子", city="札幌") と等価

アンパックは既存データの受け渡しで特に便利

すでにリストや辞書として手に入っているデータを関数に渡したいときは、アンパックを使えば 1 行で済みます。

たとえば辞書 user = {"name": "花子", "city": "札幌"}announce(**user) と書くだけで、announce(name="花子", city="札幌") と同じ意味になります。キーをひとつひとつ取り出して書き直す必要がありません。

すでに用意された cart_prices = [480, 1200, 320, 980] を、前節の calc_totalバラして渡します。

def calc_total(*prices): を再定義し、sum(prices) を返してください。

cart_prices = [480, 1200, 320, 980] を宣言し、calc_total(*cart_prices) の結果を print() してください。

print(calc_total(cart_prices)) と、アスタリスクを外して呼び出してみてください。エラーメッセージが表示されるはずなので、出力を読んでリストそのものが 1 個目の引数になっていることを確認してください。

Python エディタ

コードを実行してください

複数の戻り値 — タプルで返してアンパックで受け取る

return にはカンマで区切って複数の値を書けます。呼び出し元にはそれらを並べたタプルが返ってきます。受け取るときは a, b = 関数() と書けば、タプルをアンパックして複数の変数に一度に代入できます。

関数から複数の値を返すときに便利です(最小値と最大値、ユーザー名と権限、成功フラグとメッセージ など)。

複数の戻り値はタプルで返る
def get_person():return name, age, city(name, age, city)(タプル)result = get_person()# resultはタプルのままname, age, city= get_person()各変数(name,age,city)に展開される実体アンパック
# 複数の値をまとめて返す
def get_person():
    return "田中", 30, "東京"

# タプルのまま受け取る
result = get_person()
print(result)            # ('田中', 30, '東京')
print(type(result))      # <class 'tuple'>

# アンパックして個別の変数に
name, age, city = get_person()
print(name, age, city)   # 田中 30 東京

# 実用例: 最小値と最大値を同時に返す
def get_min_max(values):
    return min(values), max(values)

lowest, highest = get_min_max([120, 500, 300, 180])
print(f"最安 {lowest} 円 / 最高 {highest} 円")

残りをまとめたい場合は *残り を変数名の前に付ける

テストの点数のリスト return 90, 85, 78, 92, 88first, *middle, last = ... のように受け取ると、first=90last=88middle=[85, 78, 92] のように先頭・末尾だけ取り出して残りをリストにまとめることができます。「最初と最後だけ使いたい」「残りはまとめて保持したい」ときの書き方です。

商品価格のリストから、最安値と最高値を同時に返す関数を作ります。

def get_price_range(prices): を定義し、return min(prices), max(prices) で 2 つの値を返してください。

product_prices = [480, 1200, 320, 980, 650] を用意し、lowest, highest = get_price_range(product_prices)アンパックして 受け取ってください。

print(f"最安値: {lowest} 円")print(f"最高値: {highest} 円") を表示してください。

Python エディタ

コードを実行してください
QUIZ

理解度チェック

まずは1問ずつ答えてみましょう。

Q1次のコードの出力はどれですか?
def f(*args):
return sum(args)

print(f(1, 2, 3, 4))

Q2def f(**kwargs): で受け取った kwargs の型はどれですか?

Q3リスト nums = [1, 2, 3] の中身を、関数 f(a, b, c) に 3 つの位置引数として渡す書き方はどれですか?