Pythonで関数を書いているとき、「2つ以上の値をまとめて返したいが、どう書けばよいか分からない」と悩む方は少なくありません。1つの変数しか返せないと思い込んでいるケースやタプルで返せると知っていても受け取り方が分からないケースもよく見られます。
Pythonの関数では、returnで複数の値を返す方法がタプル・リスト・辞書の3パターン用意されており、それぞれ異なる特性を持っています。パターンを正しく選べないと、コードの可読性が下がったり、受け取り側の処理が複雑になったりすることがあります。
この記事では、関数から複数の戻り値を返す各パターンの書き方とコード例を詳しく解説し、受け取り方や使い分けの判断基準についても具体的に説明していきます。
Pythonのreturnで複数の戻り値を返す方法
Pythonでは、カンマ区切りの式がタプルを生成する言語仕様があり、return文にカンマ区切りで値を並べると結果的にタプルとして返されます。つまりreturn a, bはreturn (a, b)と等価です。
以下の3つのパターンを使い分けることで、状況に応じた実装ができます。
- タプルで返す方法
- リストで返す方法
- 辞書で返す方法
タプルが最も基本的な返却方法であり、リストや辞書は返した後に操作が必要な場合やキー名で管理したい場合の代替手段です。それでは各パターンについて、詳しく解説していきます。
タプルで返す方法
タプルとは、変更不可(イミュータブル)な順序付きシーケンスのことです。Pythonではカンマがタプルを構成する構文要素であり、括弧は省略可能です。
return文にカンマ区切りで値を並べるだけで、タプルとして返されます。なお、要素が1つだけのタプルを作る場合は(x,)のように末尾カンマが必須です。
Tuples are immutable, and usually contain a heterogeneous sequence of elements that are accessed via unpacking (see later in this section) or indexing
出典:Python公式チュートリアル - Tuples and Sequences
def calc_sum_avg(a, b):
"""2つの数値の合計と平均をタプルで返す関数"""
total = a + b
average = total / 2
return total, average # カンマ区切りでタプルが生成される
# アンパック(分割代入)で受け取る
result_sum, result_avg = calc_sum_avg(10, 20)
print(result_sum) # 30
print(result_avg) # 15.0
# タプルのままで受け取ることも可能
result = calc_sum_avg(10, 20)
print(result) # (30, 15.0)
print(result[0]) # 30 (インデックスアクセス)
print(type(result)) # <class 'tuple'>
上記のコードでは、return total, averageと書くだけで(30, 15.0)というタプルが返されます。受け取り側でアンパックを使うと、result_sumとresult_avgにそれぞれの値を直接代入できます。
タプルで返す方法は記述量が少なく、返す値の数が2〜3個程度で順序に意味がある場合(最小値と最大値、幅と高さなど)に特に扱いやすいパターンです。タプル自体はイミュータブルなため要素の差し替えや追加はできませんが、要素としてリストなどのミュータブルなオブジェクトを含む場合は、その内部の値は変更できる点に注意してください。
リストで返す方法
リストは変更可能(ミュータブル)な順序付きシーケンスのことで、返す要素の数が事前に決まらない場合や受け取った後に要素を追加・変更する必要がある場合に適した返却手法です。「検索結果一覧」や「フィルタ後の要素集合」のように、同種の値を可変個返すケースで特に有効です。
なお、return evens, oddsのようにカンマ区切りで書くと戻り値はタプルです。リストそのものを1つの戻り値として返す場合は、return [a, b]のようにリストリテラルで明示するか、リスト変数を単体で返します。
def split_even_odd(numbers):
"""整数リストを偶数リストと奇数リストに分けて返す関数"""
evens = [n for n in numbers if n % 2 == 0]
odds = [n for n in numbers if n % 2 != 0]
return [evens, odds] # 2つのリストを1つのリストにまとめて返す
# インデックスで受け取る
nums = [1, 2, 3, 4, 5, 6]
result = split_even_odd(nums)
print(result[0]) # [2, 4, 6]
print(result[1]) # [1, 3, 5]
# 受け取ったリストを後から変更できる
result[0].append(8)
print(result[0]) # [2, 4, 6, 8]
print(type(result)) # <class 'list'>
上記のコードでは、return [evens, odds]とすることにより、戻り値の型がリストです。result[0].append(8)のように受け取った後から要素を追加できる点が、タプルとの違いです。
タプルとリストの使い分けの基準は、返す値の性質です。異なる意味を持つ固定個の値(名前と年齢など)にはタプルが適しており、同種の値を可変個返す場合はリストが適切です。
受け取った後に要素を操作する必要がある場合もリストを選ぶと、コードの意図が明確です。
辞書で返す方法
辞書(dict)は、キーと値のペアでデータを管理するデータ構造のことで、各戻り値にキー名を付けることで可読性を高められます。インデックス番号ではなく意味のある名前でアクセスしたい場合や返す値の数が多い場合に特に適しています。
def analyze_text(text):
"""テキストを解析して文字数・単語数・行数を辞書で返す関数"""
return {
"char_count": len(text),
"word_count": len(text.split()),
"line_count": text.count("\n") + 1,
}
sample = "Hello World\nPython Return\nMultiple Values"
result = analyze_text(sample)
# キー名でアクセスする
print(result["char_count"]) # 38
print(result["word_count"]) # 6
print(result["line_count"]) # 3
# 辞書全体を表示する
print(result)
# {'char_count': 38, 'word_count': 6, 'line_count': 3}
print(type(result)) # <class 'dict'>
辞書で返す方法では、result["char_count"]のようにキー名で値を取り出せるため、戻り値の役割が一目で分かります。なお、上記の行数カウントは改行文字の個数に1を加える簡易的な算出方法であり、空文字列の場合は0行ではなく1を返す点に留意してください。
通常の辞書では、各キーに対する型をコード上で明示しにくいため、どのキーにどの型の値が入るかをドキュメント文字列(docstring)で補足するとよいでしょう。キーごとの型を明示したい場合は、Python 3.8以降で使えるtyping.TypedDictを検討してください。
以下は、3つのパターンの特性をまとめた比較表です。
| パターン | 変更可否 | アクセス | 適した場面 |
|---|---|---|---|
| タプル | 不可 | インデックス | 固定個の異なる値を返す場合 |
| リスト | 可 | インデックス | 同種の値を可変個返す場合 |
| 辞書 | 可 | キー名 | 値が多くキー名で管理したい場合 |
戻り値の数が少なく変更不要ならタプル、返した後に操作が必要ならリスト、キー名でアクセスしたい場合は辞書と覚えておくと、場面に応じた選択がしやすくなります。
Pythonで複数の戻り値を受け取る方法
タプルやリストとして返された複数の戻り値は、呼び出し側でアンパックを使って個別の変数に受け取れます。辞書で返した場合はキー名、dataclassで返した場合は属性名でアクセスするため、ここではタプルやリスト返却時の受け取り方に絞って解説します。
受け取り方には、以下の2つのパターンがあります。
- アンパックで個別の変数に受け取る方法
- 不要な戻り値を
_で捨てる方法
いずれのパターンも、シーケンス型の要素を複数の変数に分配するアンパックという仕組みを活用します。それぞれの使い方について、詳しく解説していきます。
アンパックで個別の変数に受け取る方法
アンパックとは、タプルやリストなどのシーケンス型の各要素を、複数の変数に一度に代入する構文のことです。関数がreturnで複数の値を返す場合、左辺に変数をカンマ区切りで並べるだけで各戻り値を個別の変数として受け取れます。
以下のコードは、名前と年齢を返す関数の戻り値を、アンパックで2つの変数に受け取る例です。
def get_user_info():
name = "田中太郎"
age = 30
return name, age
# アンパックで個別の変数に受け取る
user_name, user_age = get_user_info()
print(user_name) # 田中太郎
print(user_age) # 30
上記のコードでは、user_name, user_age = get_user_info()の1行で、関数が返した2つの値がそれぞれの変数に割り当てられます。アンパックを使わずにresult = get_user_info()と受け取ると、変数resultにはタプル('田中太郎', 30)がそのまま格納されるため、個別にアクセスする際はresult[0]やresult[1]のインデックス参照が必要です。
アンパックを使うと変数名で意味が明確になるため、コードの可読性が向上します。通常のアンパックでは左辺の変数の数と戻り値の要素数が一致している必要があり、一致しない場合はValueErrorが発生します。
ただし、次項で紹介する*を使ったスター式アンパックを使えば、要素数が可変でも対応できます。
| 受け取り方 | 構文例 | 特徴 |
|---|---|---|
| タプルのまま受け取る | result = func() |
インデックスで参照する必要がある |
| アンパックで受け取る | a, b = func() |
変数名で直接参照でき可読性が高い |
基本的にはアンパックを優先し、インデックス参照は動的に要素を選択する必要がある場合など限定的な場面に留めるのが望ましいです。
不要な戻り値を_で捨てる方法
関数が複数の値を返す場合でも、呼び出し元で必要なのが一部の値だけのケースがあります。そのような場合、不要な戻り値の受け取り先としてアンダースコア(_)を使うのが、Pythonの慣用的な書き方です。
_は「使わない値の受け捨て先」として広く使われる慣例的な変数名で、意図的に無視することを明示できます。
以下のコードは、3つの値を返す関数から、2番目の値だけを無視して受け取る例です。
def get_scores():
math_score = 85
english_score = 72
science_score = 90
return math_score, english_score, science_score
# 英語のスコアだけを無視して受け取る
math, _, science = get_scores()
print(math) # 85
print(science) # 90
上記のコードでは、math, _, science = get_scores()とすることによって、英語のスコアを_に受け取って実質的に捨てています。_自体は有効な変数名なので技術的にはアクセス可能ですが、「この値は使わない」という意図を読み手に伝える慣習として活用されます。
先頭や末尾の戻り値をまとめて無視したい場合は、*_を使ったスター式のアンパックも有効です。以下の例では、先頭の1つだけを取り出し、残りをまとめて捨てています。
def get_ranking():
return "1位", "2位", "3位", "4位", "5位"
# 1位だけ取り出し、残りは捨てる
first, *_ = get_ranking()
print(first) # 1位
print(_) # ['2位', '3位', '4位', '5位'] ※リストになる
*_はスターアンパック(拡張アンパック)と呼ばれる構文で、残りの要素をまとめてリストとして受け取ります。元の戻り値がタプルであっても、*付き変数は常にリストになる点に注意してください。
また、*を付けられる変数は1つのアンパック式の中で1つだけという制約があります。
| 用途 | 構文例 | 説明 |
|---|---|---|
| 中間の値を1つ捨てる | a, _, c = func() |
2番目の戻り値を無視する |
| 末尾の複数の値を捨てる | a, *_ = func() |
最初の値だけ取り出し残りを捨てる |
| 先頭の複数の値を捨てる | *_, z = func() |
最後の値だけ取り出し先頭側を捨てる |
_を使った受け取りは、コードレビューの際に「意図的に無視した」と伝わるため、不要な変数が残るよりも明確なコードです。
Pythonのreturnで複数の戻り値を返す際のよくある質問
タプルとリストのどちらで返すべきですか?
戻り値の要素数と型が固定されている場合はタプル、要素を後から追加・削除する可能性がある場合はリストを選ぶのが基本です。タプルはイミュータブル(変更不可)なため意図しない変更を防げ、関数の戻り値には特別な理由がない限りタプルが推奨されます。
戻り値が多すぎる場合はどうすればよいですか?
戻り値が4つ以上に増えてきた場合は、dataclassやtyping.NamedTupleを使って属性名を持つオブジェクトとして返すことを検討してください。いずれも型ヒントを明示でき、静的解析ツールやIDEの補完に対応するため、コードの可読性が向上します。
なお、dataclassはデフォルトではミュータブルであり、不変にしたい場合は@dataclass(frozen=True)を指定します。
複数の戻り値に型ヒントを付けるにはどう書きますか?
複数の値をタプルで返す場合、Python 3.9以降ではtuple[str, int]のように小文字で直接記述できます。3.8以前では、一般的にはfrom typing import TupleでインポートしたTuple[str, int]を使います。
型ヒントとは、関数の引数や戻り値に期待する型を注釈として記述する仕組みで、ランタイムでは強制されず静的解析ツールやIDE補完向けの記述です。
なお、tuple[str, ...]のように省略記号を使う記法は「全要素がstr型で、要素数が任意のタプル」を意味します。異なる型が混在する場合はtuple[str, int, float]のように全て列挙する必要があります。
※上記コンテンツの内容やソースコードはAIで確認・デバッグしておりますが、間違いやエラー、脆弱性などがある場合は、コメントよりご報告いただけますと幸いです。
ITやプログラミングに関するコラム
PythonをWebで実行する方法
共通テスト「情報Ⅰ」2年目で変わる、日本の教育と学び方
gitでブランチ(branch)を切り替える方法
git cloneでブランチを指定する方法
64GBのメモリが必要な人・不要な人の特徴
PCを再起動するコマンド一覧
CapsLock以外で大文字になる原因【Windows編】
パソコンで大文字になるのを解除する方法
面白いAIの活用事例を業界別に紹介
Gitでcommit(コミット)を取り消す方法
ITやプログラミングに関するニュース
サイボウズがkintone AIを正式提供、β版から約1年を経てクレジット制を導入
ロゼッタのラクヤクAIがCSRドラフト作成期間を90%以上短縮、従来4週間を約2日に
AI CROSSが不動産業界向け生成AI伴走支援を開始、アスコットの業務AI実装を実践サポート
日本情報クリエイトが「オーナー提案AIロボⅡ」売買査定を刷新、月1万円からW査定が回数無制限に
Wur株式会社がAI新規事業診断サービス「MVP事業診断レポート」をリリース、12の質問で事業構想を約10分で分析
バトンズがM&A専門家向け「AI概要書」β版を提供開始、企業概要書のドラフトを最速3分で自動生成
SCSKが観光DXサービス「Connexia」を開発、首里城公園でNFT活用の周遊促進が始動
Verdent AI発表、エンジニア不要でソフトウェアを構築する「AIエンジニアリングチーム」が登場
ゼネラルBREXAテクノロジーが外食・小売向けAIサービス「aimana」を開発、店長の意思決定をデータで支援
田中組がKencopa工程AIエージェント製品版を先行利用開始、建設現場の工程管理属人化を解消へ
