高度なパラメータとスコープ
関数の基本を学んだので、高度なテクニックを探ってみましょう。パラメータの書き方にはどのような方法があるでしょうか?「変数のスコープ」とは何でしょうか?再帰とは何でしょうか?これらの概念により、関数に対する理解が次のレベルに進みます。
1. デフォルトパラメータ
デフォルトパラメータを使うと、関数呼び出し時に引数を省略できます:
例:基本的なデフォルトパラメータ
def greet(name, greeting="Hello"):
print(f"{greeting}, {name}!")
greet("Alice") # Hello, Alice!
greet("Bob", "Hi") # Hi, Bob!
greet("Charlie", "Hey") # Hey, Charlie!
⚠️ デフォルトパラメータの落とし穴
デフォルトパラメータの値は関数定義時に一度だけ評価され、関数が呼び出されるたびに評価されるわけではありません。デフォルトがミュータブルなオブジェクト(リストや辞書など)の場合、予期しない動作を引き起こす可能性があります:
例:ミュータブルなデフォルトの落とし穴
def add_item(item, items=[]): # ❌ 危険!
items.append(item)
return items
print(add_item("apple")) # ['apple']
print(add_item("banana")) # ['apple', 'banana'] — ['banana'] ではない!
print(add_item("orange")) # ['apple', 'banana', 'orange']
なぜ? 空のリスト items=[] は関数定義時に一度だけ作成されます。後続のすべての呼び出しは同じリストに追加します。
正しい方法: デフォルトに None を使用し、関数内で新しいリストを作成します:
例:安全なデフォルトパラメータ
def add_item(item, items=None): # ✅ 安全
if items is None:
items = []
items.append(item)
return items
print(add_item("apple")) # ['apple']
print(add_item("banana")) # ['banana']
print(add_item("orange")) # ['orange']
None、数値、文字列、タプル)にすべきです。リストや辞書のようなミュータブルなオブジェクトをデフォルトとして決して使用しないでください。
2. キーワード引数
関数を呼び出すときに、パラメータ名で値を指定でき、順序を入れ替えて引数を渡せます:
例:キーワード引数
def create_user(name, age, city):
print(f"User: {name}, {age} years old, from {city}")
# 位置引数 — 順序通りに指定する必要がある
create_user("Alice", 25, "Beijing")
# キーワード引数 — パラメータ名を指定、順序は問わない
create_user(age=30, city="Shanghai", name="Bob")
create_user(name="Charlie", city="Guangzhou", age=22)
# 混合 — 位置引数を先に、キーワード引数を後に
create_user("Diana", city="Shenzhen", age=28)
# create_user(name="Eve", 35, "Chengdu") ❌ キーワード引数を位置引数の前に置けない
3. *args:可変長位置引数
関数がいくつの引数を受け取るかわからない場合は、*args を使います:
例:*args を使った合計関数
def sum_all(*numbers):
"""任意の数の数値を受け取り、その合計を返す"""
total = 0
for n in numbers:
total += n
return total
print(sum_all(1, 2)) # 3
print(sum_all(1, 2, 3, 4, 5)) # 15
print(sum_all()) # 0 — 引数なしでも動作
*args はすべての位置引数をタプルにまとめます:
例:args の調査
def show_args(*args):
print(f"Received {len(args)} arguments: {args}")
for i, arg in enumerate(args, 1):
print(f" #{i}: {arg}")
show_args("apple", "banana", "orange")
4. **kwargs:可変長キーワード引数
**kwargs は任意の数のキーワード引数を受け取り、辞書にまとめます:
例:**kwargs を使ったユーザープロファイル作成
def create_profile(**info):
"""ユーザープロファイルを作成、任意の量の情報を受け付ける"""
print("User Profile:")
for key, value in info.items():
print(f" {key}: {value}")
create_profile(name="Alice", age=25, city="Beijing", job="Engineer")
出力:
User Profile:
name: Alice
age: 25
city: Beijing
job: Engineer
例:*args と **kwargs の組み合わせ
def advanced_function(a, b, *args, **kwargs):
print(f"Positional: a={a}, b={b}")
print(f"Extra positional: {args}")
print(f"Keyword: {kwargs}")
advanced_function(1, 2, 3, 4, 5, name="Alice", age=25)
# Positional: a=1, b=2
# Extra positional: (3, 4, 5)
# Keyword: {'name': 'Alice', 'age': 25}
通常パラメータ → *args → デフォルトパラメータ → **kwargs
5. スコープ:LEGB ルール
変数はどこからアクセスできるでしょうか?これはスコープによって決まります。Python には 4 つのスコープレベルがあります:
例:LEGB スコープのネスト
# グローバルスコープ
x = 10 # グローバル変数
def outer():
# エンクロージング関数スコープ
x = 20 # これは outer のローカル x。グローバル x とは異なる
def inner():
# ローカルスコープ
x = 30 # これは inner のローカル x
print(f"inner: {x}") # 30
inner()
print(f"outer: {x}") # 20
outer()
print(f"global: {x}") # 10
LEGB の意味:
- Local — 現在の関数の内部
- Enclosing — 外側の関数のローカル変数
- Global — モジュールレベル
- Built-in — Python の組み込み関数(
print、lenなど)
Python は変数を L → E → G → B の順で検索します。
global と nonlocal
関数内でグローバル変数を変更するには、global を使います:
例:global の使用
count = 0
def increment():
global count # グローバル変数を変更する意図を宣言
count += 1
increment()
increment()
print(count) # 2
global の使用は最小限に。 グローバル変数はコードの理解とデバッグを困難にします。関数はパラメータで入力を受け取り、return で出力を返すべきであり、グローバル変数を密かに変更すべきではありません。
6. 再帰の基本
再帰は関数が自分自身を呼び出すことです。核となる考え方:大きな問題をより小さな同様のサブ問題に分割します。
例:再帰的階乗
def factorial(n):
"""n!(n の階乗)を計算"""
if n <= 1:
return 1 # 終了条件
return n * factorial(n - 1) # 再帰呼び出し
print(factorial(5)) # 120 (5×4×3×2×1)
実行過程:
factorial(5) = 5 * factorial(4)
= 5 * 4 * factorial(3)
= 5 * 4 * 3 * factorial(2)
= 5 * 4 * 3 * 2 * factorial(1)
= 5 * 4 * 3 * 2 * 1
= 120
例:フィボナッチ数列(難易度 ⭐⭐⭐)
def fibonacci(n):
"""n 番目のフィボナッチ数を返す(0 から開始)"""
if n <= 1:
return n
return fibonacci(n - 1) + fibonacci(n - 2)
# 最初の 10 個を出力
for i in range(10):
print(fibonacci(i), end=" ")
# 出力:0 1 1 2 3 5 8 13 21 34
よくあるユースケース
- デフォルトパラメータ:設定オプション、タイムアウト値、コールバック関数
*args:数学的集約関数(合計、平均)、ログフォーマット**kwargs:関数ラッパー、設定マージ、データベースクエリパラメータ- 再帰:ディレクトリツリー走査、ネストした JSON 解析、数式(階乗/フィボナッチ)
- スコープ:クロージャの理解(次のレッスン)、変数のライフタイム理解
❓ よくある質問
*args と **kwargs の名前を変更できますか?*args と kwargs は命名規則に過ぎません——重要なのはアスタリスクです。*numbers、options と書くこともできます。しかし、他の Python プログラマーがすぐに理解できるよう、慣習に従うことをお勧めします。x = 10 は「ローカル変数 x を作成する」と扱われます。本当にグローバルを変更する必要がある場合は global を使います。ただし、それは通常、設計を改善できることを意味します。📖 まとめ
- デフォルトパラメータは柔軟性を追加。ミュータブルなオブジェクトをデフォルトにしない
- キーワード引数は名前で指定可能。位置は問わない
*argsは余分な位置引数をタプルにまとめる。**kwargsはキーワード引数を辞書にまとめる- パラメータの順序:通常 →
*args→ デフォルト →**kwargs - LEGB ルールが変数の検索順序を決定:Local → Enclosing → Global → Built-in
globalは関数内からグローバル変数を変更。使用は最小限に- 再帰は関数が自分自身を呼び出すこと。終了条件と再帰条件が必要
📝 練習問題
-
基本(難易度 ⭐):関数
multiply(*nums)を書いてください。任意の数の数値を受け取り、その積を返します。引数がない場合は 0 を返します。 -
中級(難易度 ⭐⭐):関数
create_student(name, **scores)を書いてください。学生名と任意の数の科目スコア(キーワード引数)を受け取り、平均を計算して返します。呼び出し例:create_student("Alice", Chinese=85, Math=92, English=78)。 -
挑戦(難易度 ⭐⭐⭐):再帰関数
sum_digits(n)を書いてください。正の整数の各位の和を計算します。例:sum_digits(123)は1+2+3=6を返します。ヒント:10 で割った余りが最後の桁。10 で割った整数部分がそれを除去。終了条件:n < 10。



