同じ処理を何度も使うなら、関数にまとめると整理できて再利用しやすくなります。Pythonの関数はdefで定義し、returnで結果を返します。引数の渡し方が柔軟で、デフォルト値や可変長の引数も扱えます。
つまずきやすいのは、デフォルト引数に[](空リスト)や{}(空辞書)を使うと、呼び出すたびに同じものが使い回されてしまうという罠です。これはPythonでもっとも有名な落とし穴の1つです。この記事では、実機のPythonで確認しながら、関数の使い方を整理します。
- 関数は
def 名前(引数):で定義し、returnで値を返します。returnが無いとNoneが返ります。 - 引数は順番で渡す「位置引数」と、名前で渡す「キーワード引数」があります。
- デフォルト値は
def f(x, y=10):。指定しなければ既定値が使われます。 - デフォルト引数に
[]や{}を使うと共有される罠があります。Noneを使って回避します。 - 数が決まらない引数は
*args(タプル)と**kwargs(辞書)で受け取ります。 - 複数の値を返すとタプルになり、
x, y = func()で受け取れます。
関数の戻り値や引数には、これまで解説したリスト(list)や辞書(dict)がよく登場します。文字列を組み立てて返すならf-stringと文字列フォーマットもあわせて参考になります。
関数の基本(def・引数・戻り値)
関数はdefに続けて名前と引数を書き、:のあとに処理を字下げ(インデント)して書きます。結果はreturnで返します。
# 定義
def greet(name):
return f"こんにちは、{name}さん"
# 呼び出し
greet("山田") # こんにちは、山田さん
# 引数なし・戻り値なしも書ける
def hello():
print("Hello")
# return が無い関数は None を返す
result = hello() # Hello が表示され、result は None
実機でも、greet("山田")はこんにちは、山田さんを返し、returnの無い関数はNoneを返しました。「値を返したい」なら必ずreturnを書きます。
引数の渡し方(位置・キーワード)
引数は2通りの渡し方があります。順番で渡す位置引数と、名前を指定して渡すキーワード引数です。キーワードなら順番を入れ替えても正しく渡ります。
def introduce(name, age):
return f"{name}({age}歳)"
# 位置引数(定義した順番で渡す)
introduce("山田", 30) # 山田(30歳)
# キーワード引数(名前で渡す。順番は自由)
introduce(age=30, name="山田") # 山田(30歳)
# 混ぜることもできる(位置引数を先に)
introduce("山田", age=30)
実機でも、introduce(age=30, name="山田")のようにキーワードで順番を入れ替えても、正しく渡りました。引数が多い関数では、キーワードで渡すと「どの値が何か」が読み取りやすくなります。位置引数とキーワード引数を混ぜるときは、位置引数を先に書きます。
デフォルト引数
引数に既定値を設定しておくと、呼び出し時に省略できます。def f(x, y=10):のように書きます。よく使う値を既定にしておくと便利です。
def power(base, exp=2):
return base ** exp
power(3) # 9(exp は既定の 2)
power(3, 3) # 27(exp を 3 に)
# デフォルト引数は、通常の引数の後ろに置く
def greet(name, greeting="こんにちは"):
return f"{greeting}、{name}さん"
greet("山田") # こんにちは、山田さん
greet("山田", "おはよう") # おはよう、山田さん
実機でも、power(3)はexpが既定の2で9、power(3, 3)は27になりました。デフォルト引数は、通常の引数より後ろに置く必要があります(前に置くとエラーになります)。
【最重要】デフォルト引数に[]を使う罠
ここがPythonでもっとも有名な落とし穴です。デフォルト引数に[](空リスト)や{}(空辞書)を指定すると、その既定値は関数を定義したときに1度だけ作られ、呼び出しのたびに使い回されます。その結果、前回の呼び出しの内容が残ってしまいます。
# NG: デフォルト引数に [] を使うと共有される
def add_item(item, items=[]):
items.append(item)
return items
print(add_item("a")) # ['a']
print(add_item("b")) # ['a', 'b'] ← 'b' だけのはずが 'a' も残る!
# OK: None を既定値にして、中で作り直す
def add_item(item, items=None):
if items is None:
items = []
items.append(item)
return items
print(add_item("a")) # ['a']
print(add_item("b")) # ['b'] ← 毎回新しいリスト
実機で確認したところ、def add_item(item, items=[])を2回呼ぶと、2回目が['a', 'b']になり、1回目の'a'が残ってしまいました。デフォルト引数の[]は「最初に1度だけ」作られ、全ての呼び出しで共有されるためです。リストや辞書のような変更できる値(ミュータブル)をデフォルトに使うときは、代わりにNoneを既定値にし、関数の中でif items is None: items = []と作り直してください。数値や文字列のような変更できない値なら、この問題は起きません。
可変長引数(*args / **kwargs)
引数の数が決まっていないときは、*argsと**kwargsを使います。*argsは余った位置引数をタプルで、**kwargsは余ったキーワード引数を辞書で受け取ります。
# *args: 任意の数の位置引数をタプルで受け取る
def total(*args):
return sum(args)
total(1, 2, 3) # 6
total(1, 2, 3, 4, 5) # 15
# **kwargs: 任意の数のキーワード引数を辞書で受け取る
def show(**kwargs):
for key, value in kwargs.items():
print(f"{key}: {value}")
show(name="山田", age=30) # name: 山田 / age: 30
# 両方を組み合わせる(順番は args → kwargs)
def func(a, *args, **kwargs):
print(a, args, kwargs)
func(1, 2, 3, x=10) # 1 (2, 3) {'x': 10}
実機でも、*argsは(2, 3)のようにタプル、**kwargsは{'x': 10}のように辞書で受け取れました。名前のargs・kwargsは慣例で、*と**が本質です。「いくつ渡されるか分からない引数」を扱うときに使います。
複数の値を返す(タプル)
Pythonの関数は、カンマで区切って複数の値を返せます。返り値はタプルになり、受け取る側で分解(アンパック)できます。
# 複数の値を返す(カンマ区切り)
def minmax(nums):
return min(nums), max(nums)
# タプルで返るので、複数の変数で受け取れる
low, high = minmax([3, 1, 4, 1, 5])
print(low, high) # 1 5
# まとめて1つの変数で受けるとタプル
result = minmax([1, 2, 3])
print(result) # (1, 3)
print(type(result)) # <class 'tuple'>
実機でも、return min(nums), max(nums)はタプルを返し、low, high = minmax(...)で2つの変数に分けて受け取れました。「最小値と最大値」「成功フラグとデータ」のように、関連する複数の値を一度に返すのに便利です。
変数のスコープ(ローカル・global)
関数の中で作った変数は、その関数の中だけで有効なローカル変数です。関数の外の同名の変数には影響しません。外の変数を書き換えたいときはglobalを使いますが、多用は避けます。
message = "外側の値"
def change():
message = "内側の値" # これは関数内だけの別物
print(message) # 内側の値
change()
print(message) # 外側の値(関数内の代入は影響しない)
# 外の変数を関数内から変えたいときは global(多用は避ける)
count = 0
def increment():
global count
count += 1
increment()
increment()
print(count) # 2
実機でも、関数内でmessageに代入しても外のmessageは外側の値のままでした。関数は外の変数を読むことはできますが、書き換えるにはglobalが必要です。ただし、スコープをまたいだ書き換えは分かりにくくなるため、基本は引数で受け取り、戻り値で返す設計にするのがおすすめです。
型ヒントとdocstring(補足)
必須ではありませんが、引数や戻り値の型を示す型ヒントや、関数の説明を書くdocstringを付けると、読みやすく保守しやすくなります。
def add(a: int, b: int) -> int:
'''2つの整数を足して返す'''
return a + b
# 型ヒントはあくまで「目印」で、強制ではない(実行時にチェックされない)
add(2, 3) # 5
# docstring は help() や補完で説明として表示される
def greet(name: str, greeting: str = "こんにちは") -> str:
'''name にあいさつを返す。greeting で言葉を変えられる'''
return f"{greeting}、{name}さん"
型ヒント(a: intや-> int)は、コードの読み手やエディタの補完に役立ちます。ただし実行時に型をチェックするわけではないため、間違った型を渡してもエラーにはなりません(チェックには別途ツールを使います)。docstring("""...""")は、その関数が何をするかの説明で、help(関数名)で表示できます。
よくある失敗
デフォルト引数に[]や{}を使って共有される
変更できる値をデフォルトにすると、呼び出し間で使い回されます。Noneを既定値にして、関数の中で作り直します。
デフォルト引数を通常引数より前に置く
デフォルト引数は、通常の引数の後ろに置きます。前に置くとエラーになります。
returnを書かずに値が返ると思う
returnが無い関数はNoneを返します。値を返したいなら必ずreturnを書きます。
関数内の変数の変更が外に反映されないと悩む
関数内の代入はローカルです。外の変数を変えたいならglobalを使うか、戻り値で返します。
型ヒントで型がチェックされると思う
型ヒントは目印で、実行時にはチェックされません。間違った型を渡してもエラーにならない点に注意します。
よくある質問
def f(items=[])のように変更できる値をデフォルトにすると、呼び出すたびに同じリストが使い回され、前回の内容が残ります。def f(items=None)として、関数の中でif items is None: items = []と作り直すのが安全です。数値や文字列なら問題ありません。*argsは余った位置引数をタプルで、**kwargsは余ったキーワード引数を辞書で受け取ります。名前は慣例で、*と**が本質です。return a, bのようにカンマで区切ります。返り値はタプルになり、x, y = func()で複数の変数に分けて受け取れます。最小値と最大値など、関連する値を一度に返すのに便利です。global 変数名を宣言してから代入します。ただし分かりにくくなるため、基本は引数で受け取り戻り値で返す設計がおすすめです。関数内の変数は、宣言しなければローカル(その関数だけ)です。まとめ
- 関数は
def 名前(引数):で定義し、returnで返します(無ければNone)。 - 引数は位置・キーワードで渡せ、
def f(x, y=10):でデフォルト値を設定できます。 - デフォルト引数に
[]や{}を使うと共有されるので、Noneで回避します。 - 数が決まらない引数は
*args(タプル)と**kwargs(辞書)で受け取ります。 - 複数の値を返すとタプルになり、
x, y = func()で分けて受け取れます。 - 関数内の代入はローカル。外を変えるなら
globalか戻り値を使います。
Pythonの関数は、柔軟な引数の渡し方が魅力です。特に「デフォルト引数に変更できる値を使わない」という1点さえ押さえれば、初心者がハマる大きな落とし穴を回避できます。引数で受け取り戻り値で返す、という基本を意識して、再利用しやすい関数を書きましょう。
