この内容は古いバージョンです。最新バージョンを表示するには、戻るボタンを押してください。
バージョン:7
ページ更新者:guest
更新日時:2026-06-11 07:10:02

タイトル: 関数
SEOタイトル: Python 関数完全ガイド (def / lambda / 型ヒント / デコレータ / generator)

この記事の要点
  • 関数定義は def name(args):。インデント (4 スペース) でブロックを表現
  • 引数は 位置引数 / キーワード引数 / デフォルト値 / 可変長 *args / キーワード可変長 **kwargs の 5 種
  • 戻り値は return。明示しない場合は None。複数値はタプルで返せる
  • 無名関数 lambda、型ヒント def f(x: int) -> str:、スコープは LEGB (Local / Enclosing / Global / Built-in)
  • 関数は第一級オブジェクト → 変数代入・引数渡し・戻り値に使える → デコレータの基盤

関数の基本定義

Python の関数は def キーワードで定義します。インデント (PEP 8 では 4 スペース) でブロック範囲を示します。

def greet(name):
    """挨拶を返す関数 (docstring)"""
    return f"Hello, {name}!"

print(greet("Alice"))  # Hello, Alice!

# 戻り値が無い関数 (None を返す)
def log(msg):
    print(f"[LOG] {msg}")

result = log("started")
print(result)  # None

引数の 5 つのスタイル

種類書き方呼び出し例
位置引数def f(a, b):f(1, 2)
キーワード引数def f(a, b):f(a=1, b=2)
デフォルト値def f(a, b=10):f(1) → b=10
可変長位置 *argsdef f(*args):f(1, 2, 3)
可変長キーワード **kwargsdef f(**kwargs):f(x=1, y=2)
def example(a, b=10, *args, key=None, **kwargs):
    print(a, b, args, key, kwargs)

example(1)
# 1 10 () None {}

example(1, 2, 3, 4, key="abc", extra="z")
# 1 2 (3, 4) abc {'extra': 'z'}

# キーワード専用引数 (* の後はキーワード必須)
def calc(x, y, *, mode="add"):
    return x + y if mode == "add" else x - y

calc(1, 2, mode="sub")  # OK
# calc(1, 2, "sub")    # TypeError: positional 不可

戻り値とアンパック

# 複数値はタプルで返る
def minmax(nums):
    return min(nums), max(nums)

lo, hi = minmax([3, 1, 4, 1, 5])  # アンパック
print(lo, hi)  # 1 5

# dict や名前付きタプルで返す方が可読性高い
from collections import namedtuple
Result = namedtuple("Result", ["min", "max"])

def minmax2(nums):
    return Result(min(nums), max(nums))

r = minmax2([3, 1, 4])
print(r.min, r.max)

型ヒント (Python 3.5+)

型ヒントは実行時にチェックされないドキュメント・IDE 補完のための注釈です。mypy / pyright で静的解析できます。

from typing import Optional, Callable

def greet(name: str, times: int = 1) -> str:
    return f"Hello, {name}!" * times

# Optional[X] = X | None (Python 3.10+ は X | None)
def find_user(id: int) -> Optional[dict]:
    ...

# 関数型 (Callable)
def apply(fn: Callable[[int, int], int], x: int, y: int) -> int:
    return fn(x, y)

# Python 3.9+ 組込ジェネリクス
def total(nums: list[int]) -> int:
    return sum(nums)

無名関数 lambda

# 短い式専用の無名関数
square = lambda x: x * x
print(square(5))  # 25

# sorted / map / filter と組み合わせて使う
users = [{"name": "B", "age": 30}, {"name": "A", "age": 25}]
sorted(users, key=lambda u: u["age"])

# 複数行は def を使う (lambda は式 1 つのみ)

スコープと LEGB ルール

Python の名前解決は Local → Enclosing → Global → Built-in の順で探します。

x = "global"  # G

def outer():
    x = "enclosing"  # E

    def inner():
        x = "local"  # L
        print(x)     # local

    inner()
    print(x)         # enclosing

outer()
print(x)             # global

# global 宣言 でグローバル変更
counter = 0
def incr():
    global counter
    counter += 1

# nonlocal で enclosing 変更
def make_counter():
    count = 0
    def add():
        nonlocal count
        count += 1
        return count
    return add

関数は第一級オブジェクト

関数は変数に代入したり、引数として渡したり、戻り値にしたりできます。これがデコレータの基盤です。

def double(x):
    return x * 2

f = double          # 関数を変数代入
print(f(5))         # 10

# 高階関数
def twice(fn, x):
    return fn(fn(x))

print(twice(double, 3))  # 12

# クロージャ
def make_adder(n):
    def adder(x):
        return x + n
    return adder

add10 = make_adder(10)
print(add10(5))  # 15

デコレータ

import time
from functools import wraps

def timing(fn):
    @wraps(fn)             # 元関数の __name__ などを保持
    def wrapper(*args, **kwargs):
        start = time.time()
        result = fn(*args, **kwargs)
        print(f"{fn.__name__}: {time.time() - start:.3f}s")
        return result
    return wrapper

@timing
def slow():
    time.sleep(0.5)

slow()  # slow: 0.501s

# 引数付きデコレータ
def retry(times=3):
    def deco(fn):
        @wraps(fn)
        def wrapper(*args, **kwargs):
            for i in range(times):
                try:
                    return fn(*args, **kwargs)
                except Exception:
                    if i == times - 1:
                        raise
        return wrapper
    return deco

@retry(times=5)
def unstable_api():
    ...

再帰関数

def fact(n):
    if n <= 1:
        return 1
    return n * fact(n - 1)

print(fact(5))  # 120

# Python 既定の再帰深度は 1000 → 深い再帰は注意
import sys
sys.setrecursionlimit(10000)

# 反復で書ける場合は反復推奨 (末尾再帰最適化なし)
def fact_iter(n):
    result = 1
    for i in range(2, n + 1):
        result *= i
    return result

ジェネレータ (yield)

yield を含む関数はジェネレータ関数になり、巨大データを遅延評価で処理できます。

def count_up(n):
    i = 0
    while i < n:
        yield i
        i += 1

for x in count_up(3):
    print(x)  # 0, 1, 2

# メモリ効率: 巨大ファイルを 1 行ずつ処理
def read_lines(path):
    with open(path) as f:
        for line in f:
            yield line.rstrip()

for line in read_lines("huge.txt"):
    process(line)

# ジェネレータ式 (リスト内包の () 版)
squares = (x * x for x in range(1000000))

FAQ

Q: 引数のデフォルト値にリストを使うと共有される
A: def f(a=[]) はモジュール初期化時に 1 度だけ [] が作られ、呼び出し間で共有されます。def f(a=None): a = a or [] が定石です。

Q: *args**kwargs を同時に使える?
A: 可能です。並び順は def f(a, b=1, *args, c, **kwargs):*args の後は全てキーワード専用引数になります。

Q: 関数内で別の関数を定義するのは普通?
A: クロージャやデコレータでは普通です。ただし毎回呼び出しのたびに再定義されるので、性能が必要なホットパスではモジュールトップに置きます。