4.

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: クロージャやデコレータでは普通です。ただし毎回呼び出しのたびに再定義されるので、性能が必要なホットパスではモジュールトップに置きます。

編集
Post Share
子ページ

子ページはありません

同階層のページ
  1. 記述方法
  2. コメント
  3. 変数の宣言
  4. 関数
  5. 演算子
  6. 条件文
  7. 配列
  8. 連想配列
  9. ループ処理
  10. 非同期処理
  11. 同期処理
  12. 確認ウィンドウを表示する方法
  13. 文字の置換
  14. base urlを取得する方法
  15. formのsubmit前にjavascriptを呼び出す方法
  16. undefinedのイコール判定
  17. Javascript のみで form を post で submit する方法

最近更新/作成されたページ