基礎

Pythonの関数:def、return、引数、*argsと**kwargs、lambda、型注釈

更新日: · Skillaria.Top

Pythonの関数は、名前を付けたコードのまとまりで、def で定義し、名前と丸かっこで呼び出し、return で結果を返します。return がなければ None が返ります。引数は順番どおりにも名前を指定しても渡せます。*args は余分な位置引数をタプルに、**kwargs は余分な名前付き引数を辞書にまとめます。lambda は1つの式だけからなる短い無名関数を作ります。

Pythonの関数は、名前の付いたコードのまとまりです。何度でも、異なる入力を渡して呼び出せます。def で定義し、引数で値を受け取り、return で結果を返します。長いスクリプトを、分かりやすくテストしやすい手順に分ける最も基本的な方法です。

関数の定義:def

関数の定義は def で始まり、関数名、丸かっこの中の引数、コロンが続きます。本体は4つのスペースでインデントします。Pythonではインデントは飾りではなく、文法の一部です。

def greet(name):
    """ユーザーへの挨拶を返す。"""
    return f"こんにちは、{name}さん!"

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

見出しの直後に三重引用符で書いた文字列は、docstring(ドキュメント文字列)です。help(greet) やエディタのヒントに表示されます。関数名は、PEP 8の慣習に従ってsnake_caseで書きます。例:add_expense、get_rate。

def greet(name): の部分を関数のシグネチャ(署名)といいます。関数名と引数の一覧(型注釈があればそれも)から成ります。シグネチャを見れば、本体を読まなくても呼び出し方が分かります。

Pythonのreturnの働き

return は関数を終了させ、呼び出し元に値を渡します。return が実行された後のコードは実行されません。return がない場合、または値なしの return の場合、関数は None を返します。

def area(w, h):
    w * h          # 計算はされるが、結果は捨てられる

print(area(3, 4))  # None

def area(w, h):
    return w * h

print(area(3, 4))  # 12

return を書き忘れるのは、初心者に最も多いミスの1つです。関数は動くのに None が返り、エラーは次の行で見つかります。複数の値を返すこともできます。実際には1つのタプルで、分解代入で受け取れます。

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

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

早めに return すると、入れ子を減らせます。まず特殊なケースを除外し、その後に本来の処理を書きます。

def discount(price, is_member):
    if price <= 0:
        return 0
    if not is_member:
        return price
    return price * 0.9

関数の引数:位置引数と名前付き引数

パラメータは関数の定義にある名前、引数は呼び出し時に渡す値です。引数は順番に(位置引数として)渡すこともできますし、名前を指定して(名前付き引数として)渡すこともできます。名前付き引数を使うと呼び出しが自己説明的になり、順番を気にしなくてよくなります。

def add_expense(title, amount, category="その他"):
    return {"title": title, "amount": amount, "category": category}

add_expense("コーヒー", 250)                            # 位置引数
add_expense(amount=250, title="コーヒー")               # 名前付き
add_expense("タクシー", 600, category="交通費")         # 組み合わせ

デフォルト値を持つパラメータは省略できます。ルールは次のとおりです。デフォルト値のあるパラメータは、必須のパラメータの後に置きます。呼び出し時には、位置引数を名前付き引数より前に書きます。守られていないと、Pythonは SyntaxError を出します。

注意:変更可能なデフォルト値

デフォルト値は、関数が呼び出されるたびではなく、def の行が実行されたときに一度だけ評価されます。デフォルト値がリストや辞書の場合、すべての呼び出しで同じオブジェクトが共有されます。

def add_item(item, acc=[]):
    acc.append(item)
    return acc

print(add_item(1))  # [1]
print(add_item(2))  # [1, 2] — リストが前回の呼び出しを覚えている

正しい方法は、デフォルト値を None にして、関数の中で新しいオブジェクトを作ることです。

def add_item(item, acc=None):
    if acc is None:
        acc = []
    acc.append(item)
    return acc

*argsと**kwargs:任意の数の引数

パラメータの前にアスタリスク1つを付けると、余分な位置引数がすべてタプル(tuple)に集められます。アスタリスク2つを付けると、余分な名前付き引数がすべて辞書(dict)に集められます。args と kwargs という名前は慣習にすぎません。大切なのはアスタリスクの方です。

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

print(total(100, 250, 40))   # 390

def make_user(name, **kwargs):
    user = {"name": name}
    user.update(kwargs)
    return user

print(make_user("花子", city="大阪", age=30))
# {'name': '花子', 'city': '大阪', 'age': 30}

同じアスタリスクは逆方向にも使えます。関数を呼び出すときに、並びや辞書を引数に展開します。

values = [3, 4]
print(pow(*values))          # 81

opts = {"sep": " | ", "end": "\n"}
print("a", "b", **opts)      # a | b

位置専用パラメータと名前専用パラメータ

パラメータ一覧の中にある、スラッシュ / とアスタリスク * は、関門のような働きをします。/ より前のパラメータは順番に渡すしかありません(Python 3.8で導入されたPEP 570)。* より後のパラメータは名前で渡すしかありません。

def convert(amount, /, *, rate, currency="USD"):
    return round(amount / rate, 2), currency

convert(100, rate=90.5)          # (1.1, 'USD')
convert(amount=100, rate=90.5)   # TypeError: amount は位置専用
convert(100, 90.5)               # TypeError: rate は名前でのみ渡せる

名前専用のパラメータは、convert(100, 90.5, "EUR") のような、何がどの値か分かりにくい呼び出しを防ぎます。

変数のスコープ:LEGBの規則

Pythonは変数の名前を次の順で探します。Local(関数の中)→ Enclosing(外側の関数)→ Global(モジュールの最上位)→ Built-in(len のような組み込みの名前)。関数の中から大域変数を読むことはできますが、その値に新しい代入をするには global 宣言が必要です。

limit = 1000

def check(amount):
    return amount > limit    # 大域変数の読み取りは可能

def raise_limit():
    global limit
    limit = 2000             # global がないと、ローカル変数が新しく作られる

実際には global はあまり使わない方が良いでしょう。外部の状態を変更する関数はテストしにくくなります。値を引数で受け取り、結果を返すようにする方が確実です。

入れ子の関数とクロージャ

関数の中に別の関数を定義することもできます。内側の関数は、外側の関数が終わった後でも外側の変数を参照できます。これをクロージャ(閉包)といいます。こうした変数を変更するには nonlocal を使います。

def make_counter():
    count = 0
    def counter():
        nonlocal count
        count += 1
        return count
    return counter

c = make_counter()
print(c(), c(), c())   # 1 2 3

クロージャは、デコレータや関数を生成するファクトリの基礎になっています。

関数もオブジェクト

Pythonでは、関数は普通のオブジェクトです。変数に代入でき、別の関数に渡し、関数から返し、辞書に保存できます。関数には __name__、__doc__、__defaults__、__annotations__ といった属性があります。

def double(x):
    return x * 2

f = double
print(f(5), f.__name__)                # 10 double
print(list(map(double, [1, 2, 3])))   # [2, 4, 6]

Pythonのlambda:無名関数

lambda 関数は、名前のない短い関数です。本体は1つの式だけで、その結果が自動的に返されます。return は書けません。

square = lambda x: x ** 2
print(square(4))   # 16

expenses = [("コーヒー", 250), ("タクシー", 600), ("ランチ", 450)]
expenses.sort(key=lambda e: e[1], reverse=True)
print(expenses)  # [('タクシー', 600), ('ランチ', 450), ('コーヒー', 250)]

print(list(filter(lambda n: n % 2 == 0, range(10))))  # [0, 2, 4, 6, 8]

Pythonのlambdaは、sorted、max、map、filter に渡す短い引数として役立ちます。上の square のように、lambdaを変数に代入するのはPEP 8で推奨されていません。関数に名前が必要なら def を使いましょう。そうすれば、トレースバックに正しい __name__ が表示され、docstringも書けます。

deflambda
名前ありなし(<lambda>)
本体任意の数の文1つの式
return明示的暗黙的
docstring、注釈ありなし
向いている場面あらゆる場面短い key やコールバック

型注釈:ヒントであって、検査ではない

型注釈は、パラメータと戻り値に期待される型を示します。Python自身は実行時に注釈をチェックしません。x: int と書かれた関数に文字列を渡しても、そのまま動きます。注釈を検査するのは、mypy、pyright、ty などの外部ツールや、実行前にエラーを表示するエディタです。

def add_expense(title: str, amount: float, category: str = "その他") -> dict[str, object]:
    return {"title": title, "amount": amount, "category": category}

def area(w: float, h: float) -> float:
    w * h   # mypy: Missing return statement

インストールせずに検査するには、uvx mypy . を実行します。上の例は注釈の効果を示しています。-> float と書いた関数で return を忘れると、解析ツールがすぐに見つけてくれます。

Python 3.12(PEP 695)からは、ジェネリックな関数を TypeVar なしで書けます。型パラメータは角かっこで指定し、型のエイリアスは type 文で宣言します。

def first[T](xs: list[T]) -> T:
    return xs[0]

type Money = int | float

Python 3.14(PEP 649)では、注釈が遅延評価されるようになりました。注釈は最初に参照されたときに評価されるため、後で定義されるクラスを引用符なしで書けます。from __future__ import annotations も不要です。

「渡されなかった」と「None が渡された」の区別:Python 3.15のsentinel

デフォルト値が None だと、「引数が渡されなかった」のと「あえて None が渡された」のを区別できません。長年、次のような独自の目印を使って解決されてきました。

_MISSING = object()

def get_rate(currency, default=_MISSING):
    rates = {"USD": 90.5}
    if currency in rates:
        return rates[currency]
    if default is _MISSING:
        raise KeyError(currency)
    return default

Python 3.15 では、組み込みの sentinel 関数が追加されました(PEP 661)。MISSING = sentinel("MISSING") と書くと、分かりやすい repr を持つ一意のマーカーが作られます。コピーしても同じものが残り、pickleでシリアライズでき、float | MISSING のような注釈にも使えます。古いバージョンでは、object() を使う方法がそのまま使えます。

関数でよくあるミス

  • return を忘れる — 関数は None を返します。
  • かっこを忘れる — print(greet) は、呼び出し結果ではなく関数オブジェクトを表示します。
  • 変更可能なデフォルト値 — すべての呼び出しで同じリストが共有されます。
  • 引数の数の間違い — TypeError: greet() missing 1 required positional argument: 'name'。
  • 組み込みの名前と衝突する — list や sum という名前の関数は、組み込みを隠してしまいます。

まとめ

Pythonの関数は def で定義し、return で結果を返し、引数を順番どおりにも名前を指定しても受け取れます。*args と **kwargs で柔軟性を、/ と * で呼び出しの厳密さを、lambda で一度だけ使う関数の簡潔さを得られます。型注釈は、mypyと組み合わせれば実行前にエラーを見つけてくれます。講座では、この内容を実践で扱います。家計簿ツールを型注釈付きの関数に分け、mypyで検査します。

よくある質問

引数(パラメータ)と引数(アーギュメント)はどう違いますか?

パラメータは関数定義の中にある名前、引数は呼び出し時に渡す具体的な値です。def greet(name) の name はパラメータ、greet("花子") の "花子" は引数です。

関数を定義する前に呼び出せますか?

モジュールの最上位では無理です。def の行が先に実行されていないと NameError になります。ただし、別の関数の中から、その関数の定義より後に書かれた関数を呼び出すことは、その呼び出しが定義の後に行われるなら可能です。

関数は複数の値を返せますか?

形式的には1つの値ですが、その値をタプルにすれば複数の値を返せます。return a, b はタプルを返し、x, y = f() のように分解して受け取れます。

型注釈は必ず書く必要がありますか?

いいえ。注釈がなくても Python は動きます。ただし、他の人が読むコードでは、注釈があると保守が格段に楽になり、mypyやpyrightでエラーを早期に見つけられます。

lambdaの中でifは使えますか?

条件式なら使えます。例:lambda x: "偶数" if x % 2 == 0 else "奇数"。文、ループ、代入を lambda の中に書くことはできません。

動画でこのテーマを学ぶ

このテーマはコース「Python 3.15: от новичка до профи」で実践とともに詳しく解説しています。

コースへ