Python複数の戻り値を返す方法完全ガイド【初心者向け解説】

 

Pythonプログラミングにおいて、関数から複数の値を返すことは頻繁に必要になります。この記事では、Pythonで複数の戻り値を返すための様々な方法を、基本から応用まで分かりやすく解説します。

複数の戻り値とは?

多くのプログラミング関数では、1つの値しか返すことができませんが、Pythonでは複数の値を同時に返すことが可能です。これにより、1つの関数で複数の計算結果や処理結果を効率的に取得できます。

タプルを使った基本的な方法

最もシンプルな複数戻り値

def get_name_age():
    name = "田中太郎"
    age = 30
    return name, age

# 複数の値を受け取る
result_name, result_age = get_name_age()
print(f"名前: {result_name}, 年齢: {result_age}")

出力:

名前: 田中太郎, 年齢: 30

タプルとして受け取る

def calculate_basic(a, b):
    addition = a + b
    subtraction = a - b
    multiplication = a * b
    return addition, subtraction, multiplication

# タプルとして受け取る
results = calculate_basic(10, 3)
print(f"計算結果: {results}")
print(f"足し算: {results[0]}, 引き算: {results[1]}, 掛け算: {results[2]}")

出力:

計算結果: (13, 7, 30)
足し算: 13, 引き算: 7, 掛け算: 30

個別の変数に展開

def get_user_info():
    return "佐藤花子", 25, "engineer", "tokyo"

name, age, job, city = get_user_info()
print(f"{name}さん({age}歳) - {job}, {city}在住")

出力:

佐藤花子さん(25歳) - engineer, tokyo在住

辞書を使った戻り値

名前付きの戻り値

def analyze_text(text):
    words = text.split()
    return {
        "word_count": len(words),
        "char_count": len(text),
        "first_word": words[0] if words else "",
        "last_word": words[-1] if words else ""
    }

result = analyze_text("Python プログラミング 学習中")
print(f"単語数: {result['word_count']}")
print(f"文字数: {result['char_count']}")
print(f"最初の単語: {result['first_word']}")

出力:

単語数: 3
文字数: 14
最初の単語: Python

設定情報の取得

def get_database_config():
    return {
        "host": "localhost",
        "port": 5432,
        "database": "myapp",
        "username": "admin",
        "ssl_enabled": True
    }

config = get_database_config()
print(f"接続先: {config['host']}:{config['port']}")
print(f"データベース: {config['database']}")

出力:

接続先: localhost:5432
データベース: myapp

リストを使った戻り値

同じ型の複数データ

def get_fibonacci(n):
    if n <= 0:
        return []
    elif n == 1:
        return [0]
    
    fib = [0, 1]
    for i in range(2, n):
        fib.append(fib[i-1] + fib[i-2])
    return fib

numbers = get_fibonacci(8)
print(f"フィボナッチ数列: {numbers}")

出力:

フィボナッチ数列: [0, 1, 1, 2, 3, 5, 8, 13]

フィルタリング結果

def separate_numbers(numbers):
    positive = []
    negative = []
    zero = []
    
    for num in numbers:
        if num > 0:
            positive.append(num)
        elif num < 0:
            negative.append(num)
        else:
            zero.append(num)
    
    return positive, negative, zero

data = [3, -2, 0, 7, -1, 0, 5]
pos, neg, zeros = separate_numbers(data)
print(f"正数: {pos}")
print(f"負数: {neg}")
print(f"ゼロ: {zeros}")

出力:

正数: [3, 7, 5]
負数: [-2, -1]
ゼロ: [0, 0]

名前付きタプル(namedtuple)の活用

構造化された戻り値

from collections import namedtuple

# 名前付きタプルの定義
Point = namedtuple('Point', ['x', 'y'])
PersonInfo = namedtuple('PersonInfo', ['name', 'age', 'email'])

def get_coordinate():
    return Point(10, 20)

def get_person():
    return PersonInfo("山田次郎", 28, "yamada@example.com")

# 使用例
coord = get_coordinate()
print(f"座標: ({coord.x}, {coord.y})")

person = get_person()
print(f"名前: {person.name}, 年齢: {person.age}")

出力:

座標: (10, 20)
名前: 山田次郎, 年齢: 28

計算結果の構造化

from collections import namedtuple

Statistics = namedtuple('Statistics', ['mean', 'median', 'mode', 'std_dev'])

def calculate_statistics(numbers):
    import statistics as stats
    return Statistics(
        mean=stats.mean(numbers),
        median=stats.median(numbers),
        mode=stats.mode(numbers) if len(set(numbers)) != len(numbers) else None,
        std_dev=stats.stdev(numbers) if len(numbers) > 1 else 0
    )

data = [2, 4, 4, 6, 8, 8, 8]
result = calculate_statistics(data)
print(f"平均: {result.mean:.2f}")
print(f"中央値: {result.median}")
print(f"最頻値: {result.mode}")

出力:

平均: 5.71
中央値: 6
最頻値: 8

データクラスを使った戻り値

Python 3.7以降の方法

from dataclasses import dataclass

@dataclass
class UserProfile:
    username: str
    email: str
    age: int
    is_active: bool = True

def create_user_profile(username, email, age):
    return UserProfile(
        username=username,
        email=email,
        age=age,
        is_active=True
    )

profile = create_user_profile("tanaka123", "tanaka@example.com", 32)
print(f"ユーザー: {profile.username}")
print(f"アクティブ: {profile.is_active}")

出力:

ユーザー: tanaka123
アクティブ: True

計算結果のデータクラス

from dataclasses import dataclass
from typing import Optional

@dataclass
class CalculationResult:
    success: bool
    result: Optional[float]
    error_message: Optional[str] = None

def safe_division(a, b):
    if b == 0:
        return CalculationResult(
            success=False,
            result=None,
            error_message="ゼロ除算エラー"
        )
    
    return CalculationResult(
        success=True,
        result=a / b,
        error_message=None
    )

result1 = safe_division(10, 2)
result2 = safe_division(10, 0)

print(f"結果1: 成功={result1.success}, 値={result1.result}")
print(f"結果2: 成功={result2.success}, エラー={result2.error_message}")

出力:

結果1: 成功=True, 値=5.0
結果2: 成功=False, エラー=ゼロ除算エラー

実践的な活用例

ファイル処理の結果

def process_file(filename):
    try:
        with open(filename, 'r', encoding='utf-8') as file:
            content = file.read()
            lines = content.split('\n')
            word_count = len(content.split())
            
        return True, {
            "lines": len(lines),
            "words": word_count,
            "characters": len(content)
        }, None
    
    except FileNotFoundError:
        return False, None, f"ファイル '{filename}' が見つかりません"
    except Exception as e:
        return False, None, f"エラー: {str(e)}"

# 使用例(実際のファイルがない場合のデモ)
success, stats, error = process_file("nonexistent.txt")
if success:
    print(f"行数: {stats['lines']}, 単語数: {stats['words']}")
else:
    print(f"処理失敗: {error}")

出力:

処理失敗: ファイル 'nonexistent.txt' が見つかりません

Web API レスポンスの処理

def fetch_user_data(user_id):
    # 実際のAPI呼び出しをシミュレート
    if user_id == 1:
        return 200, {
            "id": 1,
            "name": "API太郎",
            "email": "api@example.com",
            "status": "active"
        }, None
    else:
        return 404, None, "ユーザーが見つかりません"

status_code, user_data, error = fetch_user_data(1)
if status_code == 200:
    print(f"ユーザー名: {user_data['name']}")
    print(f"ステータス: {user_data['status']}")
else:
    print(f"エラー {status_code}: {error}")

出力:

ユーザー名: API太郎
ステータス: active

数学計算の結果

import math

def solve_quadratic(a, b, c):
    discriminant = b**2 - 4*a*c
    
    if discriminant < 0:
        return "虚数解", None, None
    elif discriminant == 0:
        root = -b / (2*a)
        return "重解", root, root
    else:
        root1 = (-b + math.sqrt(discriminant)) / (2*a)
        root2 = (-b - math.sqrt(discriminant)) / (2*a)
        return "実数解", root1, root2

solution_type, x1, x2 = solve_quadratic(1, -5, 6)
print(f"解の種類: {solution_type}")
if x1 is not None:
    print(f"解1: {x1}, 解2: {x2}")

出力:

解の種類: 実数解
解1: 3.0, 解2: 2.0

戻り値の展開とエラーハンドリング

安全な展開

def get_user_summary(user_id):
    users = {
        1: ("田中", 30, "engineer"),
        2: ("佐藤", 25, "designer")
    }
    
    if user_id in users:
        return True, *users[user_id]
    else:
        return False, None, None, None

def safe_get_user(user_id):
    result = get_user_summary(user_id)
    success = result[0]
    
    if success:
        _, name, age, job = result
        return f"{name}さん({age}歳, {job})"
    else:
        return "ユーザーが見つかりません"

print(safe_get_user(1))
print(safe_get_user(999))

出力:

田中さん(30歳, engineer)
ユーザーが見つかりません

部分的な展開

def get_detailed_info():
    return "鈴木", 35, "manager", "osaka", "married", 2

# 必要な部分のみ取得
name, age, *_, children_count = get_detailed_info()
print(f"{name}さん({age}歳), 子供{children_count}人")

# 最初の3つのみ取得
name, age, job, *_ = get_detailed_info()
print(f"{name}さん: {job}")

出力:

鈴木さん(35歳), 子供2人
鈴木さん: manager

型ヒントでの複数戻り値

Tuple型ヒント

from typing import Tuple, Optional

def divide_with_remainder(dividend: int, divisor: int) -> Tuple[int, int]:
    quotient = dividend // divisor
    remainder = dividend % divisor
    return quotient, remainder

def safe_divide(a: float, b: float) -> Tuple[bool, Optional[float]]:
    if b == 0:
        return False, None
    return True, a / b

# 使用例
q, r = divide_with_remainder(17, 5)
print(f"17 ÷ 5 = {q} 余り {r}")

success, result = safe_divide(10, 3)
if success:
    print(f"結果: {result:.2f}")

出力:

17 ÷ 5 = 3 余り 2
結果: 3.33

Union型の活用

from typing import Union, Tuple

def parse_number(text: str) -> Tuple[bool, Union[int, float, None]]:
    try:
        # 整数として解析を試行
        if '.' not in text:
            return True, int(text)
        else:
            return True, float(text)
    except ValueError:
        return False, None

# 使用例
success1, num1 = parse_number("42")
success2, num2 = parse_number("3.14")
success3, num3 = parse_number("invalid")

print(f"結果1: {success1}, 値: {num1}, 型: {type(num1)}")
print(f"結果2: {success2}, 値: {num2}, 型: {type(num2)}")
print(f"結果3: {success3}, 値: {num3}")

出力:

結果1: True, 値: 42, 型: <class 'int'>
結果2: True, 値: 3.14, 型: <class 'float'>
結果3: False, 値: None

パフォーマンスと最適化

大量データの効率的な戻り値

def process_large_dataset(data):
    # ジェネレータを使用してメモリ効率を向上
    def filtered_data():
        for item in data:
            if item > 0:
                yield item * 2
    
    def summary_stats():
        positive_count = sum(1 for x in data if x > 0)
        negative_count = sum(1 for x in data if x < 0)
        return positive_count, negative_count
    
    processed = list(filtered_data())
    pos_count, neg_count = summary_stats()
    
    return processed, pos_count, neg_count

# 使用例
sample_data = [-2, 3, -1, 4, 0, 5, -3]
processed, positive, negative = process_large_dataset(sample_data)
print(f"処理済みデータ: {processed}")
print(f"正数: {positive}個, 負数: {negative}個")

出力:

処理済みデータ: [6, 8, 10]
正数: 3個, 負数: 3個

キャッシュを活用した効率化

from functools import lru_cache

@lru_cache(maxsize=128)
def expensive_calculation(n):
    # 重い計算をシミュレート
    import time
    time.sleep(0.01)  # 実際の処理時間をシミュレート
    
    factorial = 1
    for i in range(1, n + 1):
        factorial *= i
    
    fibonacci = [0, 1]
    for i in range(2, n):
        fibonacci.append(fibonacci[i-1] + fibonacci[i-2])
    
    return factorial, fibonacci[:n] if n > 0 else []

# 使用例
fact, fib = expensive_calculation(10)
print(f"10の階乗: {fact}")
print(f"フィボナッチ数列: {fib}")

出力:

10の階乗: 3628800
フィボナッチ数列: [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

よくあるパターンと注意点

戻り値の順序を統一

# 良い例:常に同じ順序で戻り値を返す
def get_user_info(user_id):
    # 成功時: (True, name, age, email)
    # 失敗時: (False, None, None, error_message)
    
    users = {1: ("田中", 30, "tanaka@example.com")}
    
    if user_id in users:
        name, age, email = users[user_id]
        return True, name, age, email
    else:
        return False, None, None, "ユーザーが見つかりません"

success, name, age, info = get_user_info(1)
if success:
    print(f"{name}さん({age}歳): {info}")
else:
    print(f"エラー: {info}")

出力:

田中さん(30歳): tanaka@example.com

デフォルト値の適切な使用

def analyze_string(text, include_details=False):
    basic_info = len(text), text.count(' ')
    
    if include_details:
        vowel_count = sum(1 for c in text.lower() if c in 'aiueo')
        digit_count = sum(1 for c in text if c.isdigit())
        return *basic_info, vowel_count, digit_count
    else:
        return basic_info

# 基本情報のみ
length, spaces = analyze_string("Hello World")
print(f"長さ: {length}, スペース: {spaces}")

# 詳細情報も含む
length, spaces, vowels, digits = analyze_string("Hello World 123", True)
print(f"長さ: {length}, 母音: {vowels}, 数字: {digits}")

出力:

長さ: 11, スペース: 1
長さ: 15, 母音: 3, 数字: 3

まとめ

Python複数の戻り値を返す方法は以下のような特徴があります:

基本的な方法:

  • タプル: 最もシンプルで一般的
  • 辞書: キー名で値にアクセス可能
  • リスト: 同じ型の複数データに適している

高度な方法:

  • namedtuple: 構造化されたデータに適している
  • dataclass: 型ヒント付きの構造化データ
  • カスタムクラス: 複雑なデータ構造に対応

重要なポイント:

  • 戻り値の順序と型を統一する
  • 適切な型ヒントを使用する
  • エラーハンドリングを考慮する
  • 用途に応じて最適な方法を選択する

複数の戻り値を効果的に使用することで、より柔軟で保守性の高いPythonコードを書くことができます。実際にコードを書いて、これらの手法をマスターしましょう。

関連キーワード

  • Python 複数戻り値
  • Python return 複数
  • タプル 戻り値 Python
  • 辞書 戻り値 Python
  • namedtuple Python
  • dataclass Python
  • 関数 複数返却 Python
  • Python アンパック 戻り値
  • 型ヒント 複数戻り値
  • Python 関数設計

■プロンプトだけでオリジナルアプリを開発・公開してみた!!

■AI時代の第一歩!「AI駆動開発コース」はじめました!

テックジム東京本校で先行開始。

■テックジム東京本校

「武田塾」のプログラミング版といえば「テックジム」。
講義動画なし、教科書なし。「進捗管理とコーチング」で効率学習。
より早く、より安く、しかも対面型のプログラミングスクールです。

<短期講習>5日で5万円の「Pythonミニキャンプ」開催中。

<月1開催>放送作家による映像ディレクター養成講座

<オンライン無料>ゼロから始めるPython爆速講座