Python引数展開完全ガイド【リスト・タプル・辞書のアンパック】初心者向け

 

Pythonプログラミングにおいて、リスト、タプル、辞書を関数の引数として展開(アンパック)する機能は非常に便利です。この記事では、*(アスタリスク)と**(ダブルアスタリスク)を使った引数展開について、基本から応用まで分かりやすく解説します。

引数展開(アンパック)とは?

引数展開とは、リスト、タプル、辞書などのコレクション型データを個別の引数として関数に渡す機能です。Pythonでは以下の記号を使用します:

  • *(アスタリスク):リストやタプルを位置引数として展開
  • **(ダブルアスタリスク):辞書をキーワード引数として展開

リスト・タプルの展開(*演算子)

基本的なリスト展開

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

numbers = [1, 2, 3]
result = add_three_numbers(*numbers)  # *でリストを展開
print(result)

出力:

6

タプルの展開

def greet(first_name, last_name):
    print(f"こんにちは、{first_name} {last_name}さん!")

name_tuple = ("太郎", "田中")
greet(*name_tuple)  # タプルを展開

出力:

こんにちは、太郎 田中さん!

より多くの引数を持つ例

def show_info(name, age, city, job):
    print(f"名前: {name}, 年齢: {age}, 出身: {city}, 職業: {job}")

person_data = ["佐藤", 30, "東京", "エンジニア"]
show_info(*person_data)

出力:

名前: 佐藤, 年齢: 30, 出身: 東京, 職業: エンジニア

辞書の展開(**演算子)

基本的な辞書展開

def create_profile(name, age, city):
    return f"{name}({age}歳) - {city}出身"

person_dict = {"name": "山田", "age": 25, "city": "大阪"}
profile = create_profile(**person_dict)  # **で辞書を展開
print(profile)

出力:

山田(25歳) - 大阪出身

より複雑な辞書展開

def send_email(to, subject, body, from_email="noreply@example.com"):
    print(f"送信先: {to}")
    print(f"件名: {subject}")
    print(f"本文: {body}")
    print(f"送信者: {from_email}")

email_config = {
    "to": "user@example.com",
    "subject": "重要なお知らせ",
    "body": "会議の時間が変更になりました。",
    "from_email": "admin@company.com"
}

send_email(**email_config)

出力:

送信先: user@example.com
件名: 重要なお知らせ
本文: 会議の時間が変更になりました。
送信者: admin@company.com

*と**の組み合わせ

位置引数とキーワード引数の同時展開

def complex_function(a, b, c, name="匿名", active=True):
    print(f"位置引数: {a}, {b}, {c}")
    print(f"名前: {name}, アクティブ: {active}")

args = [1, 2, 3]
kwargs = {"name": "鈴木", "active": False}

complex_function(*args, **kwargs)

出力:

位置引数: 1, 2, 3
名前: 鈴木, アクティブ: False

部分的な展開

def register_user(username, email, age=None, country="日本"):
    print(f"ユーザー: {username}")
    print(f"メール: {email}")
    print(f"年齢: {age}")
    print(f"国: {country}")

# 一部は直接指定、一部は辞書から展開
user_info = {"age": 28, "country": "アメリカ"}
register_user("johndoe", "john@example.com", **user_info)

出力:

ユーザー: johndoe
メール: john@example.com
年齢: 28
国: アメリカ

実践的な活用例

データベース接続設定

def connect_database(host, port, database, username, password, ssl=False):
    print(f"接続先: {username}@{host}:{port}/{database}")
    print(f"SSL: {'有効' if ssl else '無効'}")
    return {"status": "connected"}

# 設定を辞書で管理
db_config = {
    "host": "localhost",
    "port": 5432,
    "database": "myapp",
    "username": "admin",
    "password": "secret123",
    "ssl": True
}

connection = connect_database(**db_config)

出力:

接続先: admin@localhost:5432/myapp
SSL: 有効

APIリクエスト関数

def make_api_request(endpoint, method="GET", headers=None, params=None):
    print(f"{method} {endpoint}")
    if headers:
        print(f"ヘッダー: {headers}")
    if params:
        print(f"パラメータ: {params}")

# リクエスト設定を辞書で管理
request_config = {
    "endpoint": "/api/users",
    "method": "POST",
    "headers": {"Content-Type": "application/json"},
    "params": {"limit": 10, "offset": 0}
}

make_api_request(**request_config)

出力:

POST /api/users
ヘッダー: {'Content-Type': 'application/json'}
パラメータ: {'limit': 10, 'offset': 0}

座標計算関数

import math

def calculate_distance(x1, y1, x2, y2):
    return math.sqrt((x2 - x1)**2 + (y2 - y1)**2)

# 座標をタプルで管理
point1 = (0, 0)
point2 = (3, 4)

distance = calculate_distance(*point1, *point2)
print(f"距離: {distance}")

出力:

距離: 5.0

関数定義での可変長引数との組み合わせ

*argsを受け取る関数での展開

def sum_all(*numbers):
    return sum(numbers)

# リストを展開して渡す
number_list = [1, 2, 3, 4, 5]
total = sum_all(*number_list)
print(f"合計: {total}")

# 複数のリストを展開
list1 = [1, 2]
list2 = [3, 4]
total2 = sum_all(*list1, *list2, 5, 6)
print(f"合計2: {total2}")

出力:

合計: 15
合計2: 21

**kwargsを受け取る関数での展開

def log_event(**event_data):
    print("イベントログ:")
    for key, value in event_data.items():
        print(f"  {key}: {value}")

# 複数の辞書を展開
base_info = {"timestamp": "2024-01-01", "level": "INFO"}
event_info = {"action": "login", "user_id": 123}

log_event(**base_info, **event_info)

出力:

イベントログ:
  timestamp: 2024-01-01
  level: INFO
  action: login
  user_id: 123

ネストしたデータ構造の展開

2次元配列の展開

def print_matrix(row1, row2, row3):
    print("行列:")
    for i, row in enumerate([row1, row2, row3], 1):
        print(f"  行{i}: {row}")

matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
print_matrix(*matrix)

出力:

行列:
  行1: [1, 2, 3]
  行2: [4, 5, 6]
  行3: [7, 8, 9]

複雑なデータ構造の処理

def process_order(customer_name, items, shipping_info, payment_info):
    print(f"顧客: {customer_name}")
    print(f"商品数: {len(items)}")
    print(f"配送先: {shipping_info['address']}")
    print(f"支払い方法: {payment_info['method']}")

order_data = {
    "customer_name": "田中太郎",
    "items": ["商品A", "商品B"],
    "shipping_info": {"address": "東京都渋谷区", "date": "2024-01-15"},
    "payment_info": {"method": "クレジットカード", "amount": 5000}
}

process_order(**order_data)

出力:

顧客: 田中太郎
商品数: 2
配送先: 東京都渋谷区
支払い方法: クレジットカード

クラスメソッドでの引数展開

インスタンス作成での活用

class Person:
    def __init__(self, name, age, email, city="東京"):
        self.name = name
        self.age = age
        self.email = email
        self.city = city
    
    def display(self):
        print(f"{self.name}({self.age}歳) - {self.email} ({self.city})")

# 辞書からインスタンス作成
person_data = {
    "name": "山田花子",
    "age": 28,
    "email": "yamada@example.com",
    "city": "大阪"
}

person = Person(**person_data)
person.display()

出力:

山田花子(28歳) - yamada@example.com (大阪)

クラスメソッドでの設定管理

class DatabaseConfig:
    def __init__(self, **config):
        self.host = config.get("host", "localhost")
        self.port = config.get("port", 5432)
        self.database = config.get("database", "default")
        self.ssl = config.get("ssl", False)
    
    def show_config(self):
        print(f"DB設定: {self.host}:{self.port}/{self.database} (SSL: {self.ssl})")

config_dict = {"host": "db.example.com", "port": 3306, "ssl": True}
db_config = DatabaseConfig(**config_dict)
db_config.show_config()

出力:

DB設定: db.example.com:3306/default (SSL: True)

エラーハンドリングと展開

引数数の不一致

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

# 正常なケース
correct_list = [1, 2, 3]
print(requires_three_args(*correct_list))

# エラーケースの処理
def safe_function_call(func, args_list):
    try:
        return func(*args_list)
    except TypeError as e:
        return f"エラー: {e}"

wrong_list = [1, 2]  # 引数が不足
result = safe_function_call(requires_three_args, wrong_list)
print(result)

出力:

6
エラー: requires_three_args() missing 1 required positional argument: 'c'

辞書キーの不一致

def create_user(username, email, age):
    return f"ユーザー: {username} ({email}, {age}歳)"

def safe_dict_expansion(func, data_dict):
    try:
        return func(**data_dict)
    except TypeError as e:
        return f"キーエラー: {e}"

# 正常なデータ
good_data = {"username": "user1", "email": "user1@example.com", "age": 25}
print(create_user(**good_data))

# 不正なキー
bad_data = {"username": "user2", "invalid_key": "value", "age": 30}
result = safe_dict_expansion(create_user, bad_data)
print(result)

出力:

ユーザー: user1 (user1@example.com, 25歳)
キーエラー: create_user() missing 1 required positional argument: 'email'

動的関数呼び出し

関数名と引数を動的に決定

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

def multiply(a, b):
    return a * b

def subtract(a, b):
    return a - b

# 関数と引数の組み合わせ
operations = [
    (add, [10, 5]),
    (multiply, [3, 4]),
    (subtract, [20, 8])
]

for func, args in operations:
    result = func(*args)
    print(f"{func.__name__}({', '.join(map(str, args))}) = {result}")

出力:

add(10, 5) = 15
multiply(3, 4) = 12
subtract(20, 8) = 12

設定ベースの関数実行

def process_data(data, operation="sort", reverse=False, key=None):
    if operation == "sort":
        return sorted(data, reverse=reverse, key=key)
    elif operation == "filter":
        return [x for x in data if x > 0]
    return data

# 設定を辞書で管理
processing_configs = [
    {"operation": "sort", "reverse": True},
    {"operation": "sort", "key": len},
    {"operation": "filter"}
]

data = ["apple", "banana", "cherry", "date"]

for config in processing_configs:
    result = process_data(data, **config)
    print(f"設定 {config}: {result}")

出力:

設定 {'operation': 'sort', 'reverse': True}: ['date', 'cherry', 'banana', 'apple']
設定 {'operation': 'sort', 'key': <built-in function len>}: ['date', 'apple', 'banana', 'cherry']
設定 {'operation': 'filter'}: ['apple', 'banana', 'cherry', 'date']

よくあるエラーと対処法

1. 引数の重複

def greet(name, age):
    print(f"こんにちは、{name}さん({age}歳)")

# エラー: 同じ引数を複数回指定
# greet("田中", name="佐藤", age=30)  # TypeError

# 正しい方法
greet("田中", age=30)

2. 存在しないキーワード引数

def simple_func(a, b):
    return a + b

# エラー: 存在しないキーワード引数
data = {"a": 1, "b": 2, "c": 3}
# simple_func(**data)  # TypeError: unexpected keyword argument 'c'

# 正しい方法:必要なキーのみ抽出
filtered_data = {k: v for k, v in data.items() if k in ["a", "b"]}
result = simple_func(**filtered_data)
print(result)

出力:

3

引数展開のベストプラクティス

1. 設定管理での活用

# 設定ファイルやDBから読み込んだ設定を関数に渡す
def setup_logging(**config):
    level = config.get("level", "INFO")
    format_str = config.get("format", "%(message)s")
    filename = config.get("filename")
    
    print(f"ログ設定: レベル={level}, 形式={format_str}")
    if filename:
        print(f"ファイル出力: {filename}")

logging_config = {
    "level": "DEBUG",
    "format": "%(asctime)s - %(message)s",
    "filename": "app.log"
}

setup_logging(**logging_config)

出力:

ログ設定: レベル=DEBUG, 形式=%(asctime)s - %(message)s
ファイル出力: app.log

2. テストデータでの活用

def validate_user_data(username, email, age, active=True):
    errors = []
    if len(username) < 3:
        errors.append("ユーザー名が短すぎます")
    if "@" not in email:
        errors.append("メールアドレスが無効です")
    if age < 0:
        errors.append("年齢が無効です")
    
    return {"valid": len(errors) == 0, "errors": errors}

# テストケース
test_cases = [
    {"username": "user1", "email": "user1@example.com", "age": 25},
    {"username": "u", "email": "invalid-email", "age": -5},
    {"username": "validuser", "email": "valid@example.com", "age": 30, "active": False}
]

for i, test_data in enumerate(test_cases, 1):
    result = validate_user_data(**test_data)
    print(f"テスト{i}: {'成功' if result['valid'] else '失敗'}")
    if result['errors']:
        for error in result['errors']:
            print(f"  - {error}")

出力:

テスト1: 成功
テスト2: 失敗
  - ユーザー名が短すぎます
  - メールアドレスが無効です
  - 年齢が無効です
テスト3: 成功

3. デコレータでの活用

def retry_on_error(max_retries=3):
    def decorator(func):
        def wrapper(*args, **kwargs):
            for attempt in range(max_retries):
                try:
                    return func(*args, **kwargs)
                except Exception as e:
                    print(f"試行 {attempt + 1} 失敗: {e}")
                    if attempt == max_retries - 1:
                        raise
            return None
        return wrapper
    return decorator

@retry_on_error(max_retries=2)
def unreliable_function(x, y, should_fail=False):
    if should_fail:
        raise ValueError("意図的なエラー")
    return x + y

# 引数を展開して呼び出し
args = [10, 5]
kwargs = {"should_fail": False}
result = unreliable_function(*args, **kwargs)
print(f"結果: {result}")

出力:

結果: 15

まとめ

Python引数展開(アンパック)は以下の場面で特に有用です:

  • 設定管理: 辞書やリストで管理した設定を関数に渡す
  • 動的関数呼び出し: 実行時に決定される引数で関数を呼び出す
  • テストデータ: 複数のテストケースを効率的に実行
  • APIラッパー: 外部APIの呼び出しパラメータを管理
  • データ処理: 構造化されたデータを関数に渡す

重要なポイント:

  • *:リスト・タプルを位置引数として展開
  • **:辞書をキーワード引数として展開
  • 引数の数や名前の一致に注意
  • エラーハンドリングを適切に行う

引数展開を使いこなすことで、より柔軟で保守性の高いPythonコードを書くことができます。実際にコードを書いて、この便利な機能をマスターしましょう。

関連キーワード

  • Python 引数展開
  • Python アンパック
  • Python * **
  • リスト展開 Python
  • 辞書展開 Python
  • タプル展開 Python
  • Python 可変長引数
  • args kwargs 展開
  • Python アスタリスク
  • 引数 アンパック

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

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

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

■テックジム東京本校

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

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

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

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