コンテンツにスキップ

関数

この章で学ぶこと

  • 関数の定義と呼び出し方(def
  • 引数と戻り値
  • デフォルト引数とキーワード引数
  • 変数のスコープ(ローカル変数とグローバル変数)
  • ドキュメンテーション文字列(docstring)
  • ラムダ式の基本
  • 関数に関するよくある間違い

関数とは

関数(function)は、一連の処理をひとまとめにして名前をつけたものです。同じ処理を何度も書く代わりに、関数として定義しておけば何度でも呼び出せます。

関数を使うメリット:

  • コードの再利用: 同じ処理を何度も書かなくて済む
  • 可読性の向上: 処理に名前がつくので、何をしているかがわかりやすい
  • 保守性の向上: 処理を変更するとき、関数の中身だけ修正すればよい

関数の定義と呼び出し

基本構文

def 関数名(引数1, 引数2, ...):
    """ドキュメンテーション文字列(省略可)"""
    処理
    return 戻り値  # 省略可

引数なし・戻り値なしの関数

# 関数の定義
def greet():
    """挨拶を表示する関数"""
    print("こんにちは!")

# 関数の呼び出し
greet()  # こんにちは!
greet()  # こんにちは!(何度でも呼び出せる)

引数のある関数

引数(ひきすう)は、関数に渡すデータです。関数の処理に必要な情報を外部から受け取るために使います。

# name を引数として受け取る関数
def greet(name):
    """指定された名前で挨拶する関数"""
    print(f"こんにちは、{name}さん!")

greet("太郎")    # こんにちは、太郎さん!
greet("花子")    # こんにちは、花子さん!

複数の引数を持つ関数:

# 2 つの引数を受け取る関数
def add(a, b):
    """a と b の合計を表示する"""
    print(f"{a} + {b} = {a + b}")

add(3, 5)    # 3 + 5 = 8
add(10, 20)  # 10 + 20 = 30

戻り値のある関数

戻り値(もどりち)は、関数が処理の結果として返す値です。return 文で指定します。

# 2 つの数の合計を返す関数
def add(a, b):
    """a と b の合計を返す"""
    return a + b

# 戻り値を変数に格納して使う
result = add(3, 5)
print(result)  # 8

# 戻り値を直接式の中で使うこともできる
print(add(10, 20) * 2)  # 60

よくある間違い

return を書き忘れると None が返る:

return 文がない関数(または return だけで値を指定しない関数)は、自動的に None を返します。

# 間違い: return を書き忘れている
def add(a, b):
    a + b  # 計算しているが、結果をどこにも返していない

result = add(3, 5)
print(result)  # None(期待した 8 ではない!)

# 正しい: return で結果を返す
def add(a, b):
    return a + b

result = add(3, 5)
print(result)  # 8

print と return を混同する:

# 間違い: print は画面に表示するだけで、値を返さない
def add(a, b):
    print(a + b)  # 画面に表示されるが、戻り値は None

result = add(3, 5)  # 画面に 8 が表示される
print(result)        # None(print の戻り値は None)
print(result * 2)    # TypeError! None は計算に使えない

# 正しい: return で返して、呼び出し側で表示する
def add(a, b):
    return a + b

result = add(3, 5)
print(result)        # 8
print(result * 2)    # 16

実行例

>>> def square(n):
...     return n ** 2
...
>>> square(5)
25
>>> square(12)
144
>>> result = square(7)
>>> print(result)
49
>>> def greet(name):
...     print(f"Hello, {name}!")
...
>>> x = greet("Python")
Hello, Python!
>>> print(x)
None

複数の戻り値

Python では、タプルを使って複数の値を返すことができます。

def min_max(numbers):
    """リストの最小値と最大値を返す"""
    return min(numbers), max(numbers)

# 複数の戻り値を別々の変数で受け取る
smallest, largest = min_max([3, 1, 4, 1, 5, 9])
print(f"最小: {smallest}, 最大: {largest}")  # 最小: 1, 最大: 9

デフォルト引数とキーワード引数

デフォルト引数

引数にデフォルト値を設定しておくと、呼び出し時に省略できます。

def greet(name, greeting="こんにちは"):
    """挨拶をする。greeting を省略すると「こんにちは」になる"""
    print(f"{greeting}{name}さん!")

greet("太郎")                  # こんにちは、太郎さん!
greet("太郎", "おはよう")       # おはよう、太郎さん!

よくある間違い

デフォルト引数の順序:

デフォルト値のある引数は、デフォルト値のない引数よりも後ろに置く必要があります。

# 正しい: デフォルト引数は後ろ
def greet(name, greeting="こんにちは"):
    print(f"{greeting}{name}さん!")

# 間違い: デフォルト引数が先にある
# def greet(greeting="こんにちは", name):  # SyntaxError!
#     print(f"{greeting}、{name}さん!")

ミュータブルなデフォルト引数の罠(重要):

デフォルト引数にリストや辞書などのミュータブルな値を使うと、呼び出しのたびに同じオブジェクトが共有されてしまいます。

# 間違い: デフォルト引数にリストを使う
def add_item(item, lst=[]):
    lst.append(item)
    return lst

print(add_item("a"))  # ['a']
print(add_item("b"))  # ['a', 'b'](期待は ['b'] だが、前の呼び出しの結果が残る!)
print(add_item("c"))  # ['a', 'b', 'c'](さらに蓄積される)

# 正しい: None をデフォルトにして、関数内で新しいリストを作る
def add_item(item, lst=None):
    if lst is None:
        lst = []
    lst.append(item)
    return lst

print(add_item("a"))  # ['a']
print(add_item("b"))  # ['b'](期待通り)
print(add_item("c"))  # ['c'](期待通り)

これは Python の重要な仕様であり、面接やコードレビューでもよく問われるポイントです。

キーワード引数

関数を呼び出すとき、引数名を明示して値を渡すことができます。引数の順番を気にしなくてよくなります。

def create_profile(name, age, city):
    """プロフィールを表示する"""
    print(f"名前: {name}, 年齢: {age}, 都市: {city}")

# 位置引数(順番通り)
create_profile("太郎", 20, "横浜")

# キーワード引数(順番を変えられる)
create_profile(city="横浜", name="太郎", age=20)

変数のスコープ

スコープ(scope)とは、変数が参照できる範囲のことです。

ローカル変数

関数の中で定義された変数はローカル変数と呼ばれ、その関数の中でしか使えません。

def my_function():
    x = 10  # ローカル変数
    print(x)

my_function()  # 10
# print(x)     # NameError! x は関数の外からはアクセスできない

グローバル変数

関数の外で定義された変数はグローバル変数と呼ばれ、プログラム全体からアクセスできます。

message = "こんにちは"  # グローバル変数

def greet():
    print(message)  # グローバル変数を参照できる

greet()  # こんにちは

よくある間違い

関数内でグローバル変数を変更しようとする:

関数の中からグローバル変数の値を変更するには global 宣言が必要です。宣言なしに代入すると、同名のローカル変数が作られてしまいます。

count = 0

# 間違い: global 宣言がない
def increment():
    count += 1  # UnboundLocalError!
    # Python は count への代入を検出して「ローカル変数」と判断するが、
    # 代入前に count を読もうとしてエラーになる

# 正しい(ただし global の多用は推奨しない):
def increment():
    global count  # グローバル変数を変更することを宣言
    count += 1

increment()
print(count)  # 1

ただし、global の多用はコードを複雑にするため、できるだけ引数と戻り値を使って値をやり取りすることを推奨します。

# より良い方法: 引数と戻り値を使う
def increment(count):
    return count + 1

count = 0
count = increment(count)
print(count)  # 1

スコープの優先順位

同じ名前の変数がローカルとグローバルの両方にある場合、関数内ではローカル変数が優先されます。

x = "グローバル"

def my_function():
    x = "ローカル"    # 新しいローカル変数 x を作成(グローバルとは別)
    print(x)

my_function()  # ローカル
print(x)       # グローバル(グローバル変数は変更されていない)

実行例

>>> x = "外側"
>>> def func():
...     x = "内側"
...     print(f"関数内: {x}")
...
>>> func()
関数内: 内側
>>> print(f"関数外: {x}")
関数外: 外側

ドキュメンテーション文字列(docstring)

docstring は、関数の説明を記述するための文字列です。関数の先頭にトリプルクォートで書きます。

def calculate_bmi(weight, height):
    """BMI を計算して返す。

    Args:
        weight: 体重(kg)
        height: 身長(m)

    Returns:
        BMI の値(float)
    """
    return weight / (height ** 2)

# docstring は help() で確認できる
help(calculate_bmi)

docstring を書く習慣をつけよう

docstring を書いておくと、後から関数の使い方を確認しやすくなります。特に引数の意味や戻り値の説明を書いておくと、コードの可読性が大幅に向上します。最低限、関数が何をするかの 1 行の説明は書くようにしましょう。


ラムダ式(lambda)

ラムダ式は、小さな無名関数(名前のない関数)を 1 行で定義する構文です。

# 通常の関数定義
def double(x):
    """x を 2 倍にする"""
    return x * 2

# ラムダ式で同じ関数を書く
double = lambda x: x * 2

print(double(5))  # 10

ラムダ式は sorted()map() などの関数に直接渡す場合に便利です。

# リストを特定の基準でソートする
students = [("太郎", 85), ("花子", 92), ("次郎", 78)]

# 成績(タプルの 2 番目の要素)でソート
students_sorted = sorted(students, key=lambda s: s[1])
print(students_sorted)
# [('次郎', 78), ('太郎', 85), ('花子', 92)]

ラムダ式の使いどころ

ラムダ式は簡単な処理に限って使います。複雑な処理は通常の def で定義した方が読みやすくなります。


実践例: 関数を組み合わせたプログラム

def is_even(n):
    """n が偶数かどうかを判定する"""
    return n % 2 == 0

def filter_even(numbers):
    """リストから偶数だけを取り出す"""
    result = []
    for num in numbers:
        if is_even(num):
            result.append(num)
    return result

def calculate_average(numbers):
    """リストの平均値を計算する"""
    if len(numbers) == 0:
        return 0
    return sum(numbers) / len(numbers)

# メインの処理
data = [3, 8, 12, 5, 7, 20, 1, 16]

even_numbers = filter_even(data)
print(f"偶数: {even_numbers}")            # 偶数: [8, 12, 20, 16]

avg = calculate_average(even_numbers)
print(f"偶数の平均: {avg}")               # 偶数の平均: 14.0

よくある間違いのまとめ

よくある間違い

1. return を忘れて None が返る:

# 間違い
def multiply(a, b):
    a * b  # 計算はされるが結果が返されない

result = multiply(3, 4)
print(result)  # None

# 正しい
def multiply(a, b):
    return a * b

result = multiply(3, 4)
print(result)  # 12

2. 関数を呼び出さずに参照だけしてしまう:

def greet():
    print("こんにちは!")

# 間違い: () をつけていないので関数オブジェクトへの参照になる
greet      # 何も起きない(関数を呼び出していない)
print(greet)  # <function greet at 0x...>

# 正しい: () をつけて呼び出す
greet()    # こんにちは!

3. ミュータブルなデフォルト引数:

# 間違い
def append_to(element, target=[]):
    target.append(element)
    return target

# 呼び出しのたびにリストが蓄積される
print(append_to(1))  # [1]
print(append_to(2))  # [1, 2](期待は [2])

# 正しい
def append_to(element, target=None):
    if target is None:
        target = []
    target.append(element)
    return target

print(append_to(1))  # [1]
print(append_to(2))  # [2]

4. スコープの混乱(ローカル変数とグローバル変数):

x = 10

def func():
    # 間違い: x を参照した後に代入しようとしている
    # print(x)  # UnboundLocalError
    # x = 20    # この代入があるため、Python は x をローカル変数と見なす

    # 正しい方法 1: 引数で受け取る
    pass

def func(x):
    print(x)
    x = 20  # ローカル変数 x を変更(グローバルには影響しない)
    return x

5. 引数の数が合わない:

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

# 間違い: 引数が足りない
# add(5)       # TypeError: add() missing 1 required positional argument: 'b'

# 間違い: 引数が多すぎる
# add(1, 2, 3) # TypeError: add() takes 2 positional arguments but 3 were given

# 正しい:
add(5, 3)      # 8

まとめ

  • 関数def で定義し、処理を再利用可能な単位にまとめる
  • 引数で関数にデータを渡し、戻り値return)で結果を受け取る
  • return がない関数は None を返す。printreturn は役割が異なる
  • デフォルト引数を設定すると、呼び出し時に引数を省略できる。ただしミュータブルな値をデフォルトにしてはならない
  • 関数内の変数はローカル変数であり、関数の外からはアクセスできない
  • docstring で関数の説明を書く習慣をつけると、コードの可読性が向上する
  • ラムダ式は小さな無名関数を 1 行で書くための構文
  • 関数を呼び出すときは必ず () をつけること。ff() は別物