Skip to content
This page has been auto-translated and may contain errors.View in English

関数

プログラムが大きくなるにつれて、同じロジックを複数の場所で書くことになります。関数を使えば、ロジックを一度だけ書いて名前を付け、どこでも使うことができます。一箇所を修正すれば、すべての呼び出し箇所に自動的に反映されます。

関数はコードの再利用と抽象化の主要な単位です。動作をカプセル化し、名前を付け、明確なインターフェース(パラメータと戻り値)を定義し、どこからでも呼び出せるようにします。適切な名前の関数はドキュメントとしても機能します。validate_email() は、コードを読まなくてもそのブロックが何をするかを教えてくれます。

Python では、def は関数オブジェクトを作成し、それを現在のスコープ内の名前に束縛します。関数はファーストクラスオブジェクトです。変数に代入したり、コレクションに格納したり、引数として渡したり、他の関数から返したりできます。クロージャは囲んでいるスコープから自由変数を捕捉します。これを理解すると、高階プログラミングが自然になります。

python
def greet(name):
    return f"Hello, {name}!"

print(greet("さくら"))   # "Hello, さくら!"
print(greet("ひろし"))   # "Hello, ひろし!"

一度書けば、どこでも使え、一箇所で修正できます。

関数の定義

def キーワードで関数定義を開始し、名前、丸括弧、コロン、そしてインデントされた本体が続きます。関数は呼び出されるまで何もしません。def で定義し、その後 () を付けて名前で呼び出します。

def は関数オブジェクトを作成し、それを現在のスコープの指定された名前に束縛する文です。本体は定義時には実行されません。関数が呼び出されたときにのみ実行されます。return 文のない関数は暗黙的に None を返します。

def は複合文で、関数本体をコードオブジェクトにコンパイルし、新しい関数オブジェクトを現在の名前空間の名前に束縛します。関数オブジェクトには、そのコードオブジェクトへの参照、デフォルト引数の値、囲んでいるスコープへの参照(クロージャ用)が格納されます。本体は呼び出し時にのみ実行されます。

python
def say_hello():
    print("Hello!")

say_hello()   # 関数を呼び出す

パラメータと引数

パラメータは関数が期待する入力です。丸括弧の中にリストします。関数を呼び出すと、渡した値が順番にパラメータに対応付けられます。

パラメータは関数のインターフェースを定義します。引数は呼び出し時に渡される具体的な値です。位置引数は位置によって対応付けられ、キーワード引数は名前によって対応付けられます。デフォルト値を付けるとパラメータをオプションにできます。

Python には4種類のパラメータがあります: 位置またはキーワード(デフォルト)、キーワード専用(* の後)、位置専用(/ の前、Python 3.8+)、可変長(*args**kwargs)です。呼び出し時には、位置引数は左から右に束縛され、キーワード引数は名前で束縛されます。競合は TypeError を発生させます。デフォルト値は関数の定義時に一度だけ評価され、呼び出しごとではありません。

python
def greet(name, greeting):
    print(f"{greeting}, {name}!")

greet("さくら", "Hello")    # "Hello, さくら!"
greet("ひろし", "Hi")       # "Hi, ひろし!"

パラメータと引数

パラメータ は関数定義の中の名前です。引数 は関数を呼び出すときに実際に渡す値です。実際には、これらの言葉は同じ意味で使われます。ドキュメントを読むときには区別があることを意識しておきましょう。

デフォルト値

パラメータにデフォルト値を与えることができます。呼び出し側がその引数を提供しなかった場合、デフォルト値が使われます。デフォルト値のあるパラメータは、デフォルト値のないパラメータの後に来る必要があります。

デフォルト値はパラメータをオプションにします。これらは定義時に一度だけ評価され、呼び出しごとではありません。これはミュータブルなデフォルト値で問題になります。def f(items=[]) はすべての呼び出しで同じリストを共有します。修正方法は、デフォルトとして None を使用し、関数本体内でリストを作成することです。

デフォルト値は関数オブジェクトに f.__defaults__ (位置)と f.__kwdefaults__ (キーワード専用)として格納されます。これらは def の実行時に一度だけ評価され、呼び出しごとではありません。ミュータブルなデフォルト値の罠(def f(x=[]))は古典的な Python の落とし穴です。リストは一度作成され、呼び出しを跨いで in-place で変更されます。イディオマティックな修正方法: def f(x=None): if x is None: x = []

python
def greet(name, greeting="Hello"):
    print(f"{greeting}, {name}!")

greet("さくら")           # "Hello, さくら!"
greet("さくら", "Hi")     # "Hi, さくら!"

デフォルト値のあるパラメータは、デフォルト値のないパラメータの後に来る必要があります。

キーワード引数

関数を呼び出すとき、引数に名前を付けることができます。これにより、特に多くのパラメータを持つ関数では呼び出しが読みやすくなり、任意の順序で渡すことができます。

キーワード引数は関数呼び出しを自己文書化します。位置とキーワードを混在させることができます。位置引数が先に来る必要があります。ブール値フラグや同じような型の多くのパラメータを持つ関数では、キーワード引数は引数を間違った順序で渡すことによる気付きにくいミスを防ぎます。

キーワード引数は位置ではなく名前で束縛されます。位置引数は呼び出し時にキーワード引数の前に来る必要があります。同じ引数を位置とキーワードの両方で渡すと TypeError が発生します。キーワード専用の引数を強制するには、パラメータリストの中で裸の * の後に配置します: def f(a, *, b)b をキーワード専用にします。

python
def describe_player(name, score, level):
    print(f"{name} | Score: {score} | Level: {level}")

describe_player("さくら", 87, 5)                        # 位置
describe_player(name="さくら", level=5, score=87)       # キーワード、任意の順序
describe_player("さくら", level=5, score=87)            # 混合: 位置が先

戻り値

return は呼び出し側に値を送り返します。return がない場合、関数は None を返します。return が実行されると、関数は直ちに終了します。そのブロック内のそれ以降のコードはスキップされます。

return は関数を終了し、呼び出し側に値を渡します。明示的な return のない関数は暗黙的に None を返します。return は関数本体のどこにでも現れることができ、複数回使うこともできます。最初に到達したものが関数を終了させます。これにより、ガード節としての早期リターンが有用になります。

returnRETURN_VALUE バイトコードを発行し、スタックの最上位をポップして呼び出し側のフレームに渡します。最後まで実行された関数は暗黙的に None を返します。複数の return 文は問題なく、複雑な条件を持つ単一のリターンよりもしばしばすっきりします(「早期リターン」パターン)。try ブロック内の return も関連する finally 節を実行します。

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

result = add(3, 4)   # result = 7
print(result)

return はまた、関数を直ちに終了させます。そのブロック内のそれ以降のコードは実行されません。

複数の値を返す

Python ではカンマで区切ることで複数の値を返すことができます。呼び出し側はそれらをタプルとして受け取り、一行で別々の名前にアンパックできます。

カンマで複数の値を返すと、それらはタプルにパックされます。呼び出し側は対応する名前でアンパックします。これは複数の結果を自然に生成する関数のためのイディオマティックな Python です。これは特別な機能ではなく、タプルのパッキングとアンパッキングです。

return a, b は暗黙的なパッキングによって値をタプルにパックします。呼び出し側は x, y = f() でアンパックし、これは返されたタプルに対して __iter__ を呼び出します。型ヒントを明確にするために、戻り値の型を tuple[int, str] として注釈するか、名前付きタプルを使用します。プレーンなタプルを返すのは少数の場合は問題ありませんが、2つか3つ以上の値の場合は、名前付きタプルかデータクラスを検討してください。

python
def min_max(numbers):
    return min(numbers), max(numbers)

low, high = min_max([3, 7, 1, 9, 4])
print(low, high)   # 1 9

low, high = ... という構文は アンパッキング です。Python は返された各値を対応する名前に代入します。

スコープ

関数内で作成された変数は、その関数内にのみ存在します。外からは見えません。すべての関数の外で定義された変数はどこからでも見えますが、明示的な宣言なしに関数の中から変更することはできません。

Python には ローカルスコープ (関数内)、囲んでいるスコープ (ネストされた関数の場合、外側の関数内)、グローバルスコープ (モジュールレベル)、ビルトインスコープ があります。名前の検索はその順序で LEGB ルールに従います。ローカル変数は同じ名前の外側の変数をシャドウします。global は名前がモジュールレベルの束縛を参照することを宣言します。

Python の LEGB 名前解決: Local、Enclosing (クロージャ)、Global (モジュール)、Built-in です。各 def は新しいローカル名前空間を作成します。グローバル変数の読み取りは自動的に動作しますが、書き込みにはローカルシャドウを作成しないように global x が必要です。nonlocal x は最も近い囲んでいる(非グローバル)スコープにアクセスし、クロージャが捕捉された変数を変更できるようにします。global の使いすぎはコードの推論を困難にします。パラメータと戻り値を優先してください。

python
def calculate():
    result = 42   # この関数のローカル
    return result

calculate()
print(result)   # NameError、result はここに存在しない
python
count = 0

def increment():
    global count    # グローバルを変更したいと宣言する
    count += 1

increment()
print(count)   # 1

global の使用は最後の手段にすべきです。コードの推論が難しくなります。値を渡して戻り値で返すことを優先してください。

*args と **kwargs

関数がいくつの引数を受け取るかわからないことがあります。*args は任意の数の位置引数をタプルに集めます。**kwargs は任意の数のキーワード引数を辞書に集めます。argskwargs という名前は慣習であり、重要なのはアスタリスクです。

*args は余分な位置引数をタプルに集めます。**kwargs は余分なキーワード引数を辞書に集めます。両方とも通常のパラメータと組み合わせることができます。通常のパラメータが最初、次に *args、次にキーワード専用パラメータ、最後に **kwargs です。これらは、引数を別の関数に渡すラッパー関数に便利です。

*args は残りの位置引数から tuple を作成します。**kwargs は残りのキーワード引数から dict を作成します。パラメータの順序: 位置またはキーワード、*args、キーワード専用、**kwargs。呼び出し側では、*iterable は位置引数をアンパックし、**mapping はキーワード引数をアンパックします。これらは対称的です: シグネチャの中の * は集め、呼び出し側の * はアンパックします。

python
def total(*args):
    return sum(args)

total(1, 2, 3)          # 6
total(1, 2, 3, 4, 5)   # 15
python
def display(**kwargs):
    for key, value in kwargs.items():
        print(f"{key}: {value}")

display(name="さくら", score=87, level=5)

通常のパラメータと組み合わせることができます。通常のパラメータが最初に来ます:

python
def describe(title, *tags, **metadata):
    print(f"{title} | tags: {tags} | meta: {metadata}")

describe("Python intro", "beginner", "python", author="さくら", year=2024)

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

docstring は関数の先頭にある文字列で、その関数が何をするかを記述します。Python のエディタやツールはこれを使って、関数呼び出しにホバーしたときにヘルプを表示します。三重引用符を使い、シンプルな関数には1行で書きます。

docstring は f.__doc__ として格納され、help() で表示されます。慣習として1行のサマリー、その後に必要に応じて空行と詳細を続けます。公開される関数では、docstring はツールが取り出せるドキュメントです。複数の場所から呼び出される関数では必須です。

docstring は関数、クラス、またはモジュールの本体の最初の文として配置される文字列リテラルです。それらはオブジェクトに __doc__ として格納されます。PEP 257 が慣習を定義しています。Sphinx、pydoc、IDE などのツールはすべて __doc__ に依存しています。docstring 内の型情報については、Google、NumPy、または Sphinx スタイルを使用します。現代のコードでは、docstring 内の型注釈よりもシグネチャ内の型ヒントが推奨されます。

python
def normalise(value, min_val, max_val):
    """既知の最小値と最大値が与えられたときに、値を 0-1 の範囲にスケールする。"""
    return (value - min_val) / (max_val - min_val)
python
def build_url(base, version, resource, *, secure=True):
    """
    API エンドポイント URL を構築する。

    完全修飾の URL 文字列を返す。secure が False の場合、
    URL は https の代わりに http を使用する。
    """
    scheme = "https" if secure else "http"
    base   = base.replace("https://", "").replace("http://", "")
    return f"{scheme}://{base}/{version}/{resource}"

名前とシグネチャから明らかに自己説明的でない関数には、docstring を書きましょう。

型ヒント

型ヒントを使うと、関数が期待する型と返す型を注釈できます。Python は実行時にこれを強制しませんが、エディタはこれを使って実行する前に間違いを検出します。コロンの前の -> は戻り値の型を指定します。

型ヒントはツールが検証するドキュメントです。エディタや型チェッカー(mypy、pyright)は、実行時前に型の不一致を検出するためにこれを使います。標準の Python では実行時の効果はありません。-> None は戻り値のない関数に対する正しい注釈です。汎用コンテナには list[int]dict[str, int] を使用します(Python 3.9+)。

型ヒントは実行時に typing.get_type_hints() で処理されますが、実行に直接の効果はありません。静的型チェッカーは実行時前にそれらを分析します。PEP 484 がアノテーションシステムを導入し、PEP 585 は Python 3.9+ で typing をインポートせずに list[int] のようなビルトインジェネリックを許可しました。関数シグネチャの -> None は「何も返さない」ことと「式コンテキストで使用してはならない」ことの両方を示します。複雑な型については、typing.Protocoltyping.TypeVartyping.overload が完全な静的型付けの力を提供します。

python
def greet(name: str, score: int) -> str:
    return f"{name} scored {score}"
python
def log(message: str) -> None:
    print(f"[LOG] {message}")
python
def top_scores(scores: list[int], n: int) -> list[int]:
    return sorted(scores, reverse=True)[:n]

型ヒントはオプションですが、複数の場所から呼び出される関数では有用です。これらはツールが検証できるドキュメントです。

値としての関数

Python の関数は文字列や数値と同じように値です。変数に代入したり、他の関数に渡したりできます。これが sorted()key= 関数を受け取る仕組みです。

関数はファーストクラスオブジェクトです。型(function)を持ち、変数やコレクションに格納でき、引数として渡したり値として返したりできます。これは sorted(key=...)map()filter() のような高階関数の基礎となっています。

関数は function 型のオブジェクトで、属性として __name____doc____annotations____defaults____code____closure__ を持ちます。コピーされるのではなく参照で渡されます。関数から関数を返す場合、内部関数が外側のスコープから変数を参照していればクロージャを作成します。これらの変数は f.__closure__ に格納されます。

python
def double(x):
    return x * 2

def apply(func, value):
    return func(value)

apply(double, 5)   # 10

引数として関数を渡すことは、sorted()map()filter() で常に出てきます。Lambda、内包表記、zip の章でも見ることになります。

実践

連携する2つの関数: letter_grade はスコアを文字に変換し、summarise はリスト内のすべてのスコアに対してそれを呼び出します:

python
def letter_grade(score: int) -> str:
    if score >= 90:
        return "A"
    elif score >= 80:
        return "B"
    elif score >= 70:
        return "C"
    else:
        return "F"

def summarise(scores: list[int]) -> None:
    total  = sum(scores)
    avg    = total / len(scores)
    grades = [letter_grade(s) for s in scores]
    print(f"Average: {avg:.1f}")
    print(f"Grades: {', '.join(grades)}")

summarise([87, 92, 74, 65, 91])

ログフォーマッタと、それを使うファイルプロセッサ。明示的に無効にしない限り副作用を防ぐ dry_run デフォルトパラメータを持ちます:

python
def format_log(level: str, message: str) -> str:
    return f"[{level.upper():5}] {message}"

def process_file(path: str, dry_run: bool = True) -> bool:
    print(format_log("info", f"Processing {path}"))
    if dry_run:
        print(format_log("info", "Dry run, no changes made"))
        return True
    return True

process_file("report.csv")
process_file("report.csv", dry_run=False)

単一値の正規化関数と、その上に構築された列の正規化関数。型ヒントと docstring 付きです。列の関数は範囲を一度計算し、各項目に対してスカラー関数を再利用します:

python
def normalise(value: float, min_val: float, max_val: float) -> float:
    """既知の最小値と最大値が与えられたときに、値を 0-1 の範囲にスケールする。"""
    if max_val == min_val:
        return 0.0
    return (value - min_val) / (max_val - min_val)

def normalise_column(values: list[float]) -> list[float]:
    """値の列全体を正規化する。"""
    lo, hi = min(values), max(values)
    return [normalise(v, lo, hi) for v in values]

raw = [10.0, 25.0, 5.0, 40.0, 15.0]
print(normalise_column(raw))

ここでの型ヒントには2つの目的があります: 関数が何を期待するかを文書化することと、誤って文字列のリストを渡す呼び出し側を型チェッカーが検出できるようにすることです。