関数とは?メソッドとは?
Pythonを学び始めると、「関数」と「メソッド」という用語が出てきます。
どちらも何らかの処理を施すものですが、「何が違うの?」「どう使い分けるの?」と疑問に思うことがあります。
例えば、リストを並び替えたいときに登場する以下2つのコード:
numbers = [3, 1, 2]
sorted_numbers = sorted(numbers) # 関数
numbers.sort() # メソッド
一見似たような処理ですが、使い方も結果も少し違います。
ここに「関数とメソッドの違い」が現れています。
この違いを理解しておくことで、Pythonのコードをより深く読み解く力がつき、自分でも自由に関数やメソッドを使いこなせるようになります。
また、Pythonにおいて重要な概念である「オブジェクト指向」の理解の一助になります。
つまづきやすいところですが、ぜひ最後まで読んでみてください。
以下の比較表が理解できるようにしましょう!
関数とは何か? 〜 len()
や print()
の仲間たち 〜
関数とは
まず、関数とは何かを解説します。
関数とは、「何か(引数)を渡すと、処理結果(戻り値)を返してくれる処理のまとまり」です。
イメージ図は以下の通りです。
Pythonにはlen()
、sorted()
、max()
など、最初から使える関数(組み込み関数)がたくさん用意されています。
図の通り、同じ引数を渡しても、関数によって全く異なる戻り値が返ってきます。
Pythonにおいてデータの型を意識することが重要だということは、別記事でも何度か強調しています。
関数の使用においても、各関数が「どんな型のデータを処理対象として渡すと、どんな型のデータを返すのか」、を理解しておくことが非常に大事になります。
関数の使い方
関数の使い方は下図の通り。
引数の数は関数によって異なりますが、多くの関数では最初の引数に処理対象、その後の引数で処理の挙動を調整する条件を指定します。
print(len("python")) # 6と出力
print(sorted("python")) # ['h', 'n', 'o', 'p', 't', 'y']と出力
print(sorted("python", reverse=True)) # ['y', 't', 'p', 'o', 'n', 'h']と出力
print(max("python")) # "y"と出力
メソッドとは何か? 〜 オブジェクトが持つ“関数っぽいもの” 〜
メソッドとは
メソッドとは、「オブジェクトに結びついた関数」です。
いやいや、「オブジェクト」って何ぞや?
当然の疑問です。
ここで言う「オブジェクト」とは、数値(int / float型)や文字列(str
型)、リスト(list
型)などのデータのまとまりのことです。
Pythonでは、数値・文字列・リスト・辞書など、あらゆるデータが「オブジェクト」として扱われています。
これがPythonが「オブジェクト指向」と呼ばれる所以です。
例えば、「a = 1」で定義した変数aは数値型のオブジェクト、「b = [1, 2, 3]」で定義した変数bはリスト型のオブジェクト、となります。
本当にすべてがオブジェクトなんです。
そして、これらのオブジェクトはそれぞれ固有の関数(=メソッド)を持っているということです。
文字列型のオブジェクトは文字列型固有のメソッドを、リスト型のオブジェクトはリスト型固有のメソッドを持っています。
これは、コードを書くときに型を意識しておく必要がある理由の一つです。
データ型によって使えるメソッドが違うので。
メソッドの使い方
メソッドの使い方は下図の通り。
処理対象オブジェクトの後にピリオドをつけてメソッドを呼び出します。
引数には処理の挙動を調整する条件を指定しますが、何も指定しなければデフォルト条件で処理を行うメソッドが多いです。
—
また、重要なことですが、メソッドを呼び出すためには処理対象のオブジェクトを正しく生成しておく必要があります。
図の例では変数aにリストを代入していますが、これでリストオブジェクトが変数aに格納され、正しく生成されたことになります。
オブジェクトの生成を「インスタンス化」と呼んだりします。
メソッドは“オブジェクトありき”で成り立つ
メソッドは、これまでの説明の通り、あくまで「オブジェクトに属する関数」です。
そのため、オブジェクトが存在しなければメソッドを呼び出すことすらできません。
例えば、次のようなコードを考えてみましょう:
[1, 2, 3].append(4)
リストに要素を追加するappend()メソッドを使おうとするコードです。
期待するデータは4が追加された [1, 2, 3, 4] というリストです。
一見うまくいきそうですが、これでは期待するデータは得られません。(Noneが返ってきます)
なぜなら、前項で触れたように、処理対象となるオブジェクトを正しく生成せずにメソッドを呼び出してしまっているからです。
以下コードのように、オブジェクトを生成(≒変数に保持)し、それに対してメソッドを使う必要があります:
numbers = [1, 2, 3]
numbers.append(4)
print(numbers) # [1, 2, 3, 4]
このように、メソッドは「誰に向けた命令か」が明確である必要があります。
オブジェクトが存在して初めて、メソッドが機能するのです。
戻り値と副作用の違いに注意しよう
副作用とは
代表的な関数にprint関数があります。
代表的な関数のくせに、こいつは関数の中では特殊です。
基本的に関数は「何かを返す = 戻り値がある」処理ですが、print関数は何かを “返す” のではなく、何かを “起こすだけ” です。
print関数は “画面への出力” という動作を “起こすだけ” ですね。
こういった関数は戻り値が存在しない、もっと正確に言うと戻り値が “None” です。
これをプログラミングでは副作用(side effect)と呼んだりします。
何かを “返す” 処理なのか、”起こすだけ” の処理なのかを理解しておくことは非常に重要です。
関数はprint関数のように反例はありますが、多くの場合は何かを返します。
一方、メソッドは副作用を目的としたものも多く存在します。
破壊的、非破壊的
戻り値があるのか、副作用を目的としているのかを理解していないと、エラーや意図していない状況につながる場合があるので注意が必要です。
リストデータの並び替えを例に見てみましょう。
リストデータの並び替えは組み込み関数、あるいはリスト型のメソッド、どちらでも実装できます。
ただし、関数の場合は戻り値があり、メソッドの場合は副作用のみ、という違いがあります。
関数のsorted()は並び替えたリストを戻り値として返すため、元のリストはそのまま保持されます。
プログラミングでは、これを非破壊的と表現します。
一方、メソッドのsort()は戻り値が無く、リストそのものを並び替えるという動作を “起こすだけ” です。
つまり、元のリストそのものが変更されるため、元のリストはもう存在しません。
プログラミングでは、これを破壊的と表現します。
# 関数:元のリストはそのまま、新しいリストが返る
numbers = [3, 1, 2]
sorted_numbers = sorted(numbers)
print(numbers) # [3, 1, 2]
print(sorted_numbers) # [1, 2, 3]
# メソッド:リストそのものが変更される
numbers.sort()
print(numbers) # [1, 2, 3]
関数やメソッドを使うときは、戻り値があるのかどうか、副作用だけの場合はどういう動作なのか、を把握しておくことが重要です。
もちろん関数ごと、メソッドごとにそれを覚える必要はありません。
そういう違いがあることを理解しておいて、使うときに気にするだけで十分です。
関数とメソッドの適用範囲の違い
関数はいろいろなオブジェクトを処理対象に取れるケースが多い、つまり適用範囲が比較的広いです。
一方、メソッドは解説してきた通り、オブジェクトに結び付いているため、適用できるオブジェクトが決まっています。
例えば、len()関数は文字列型やリスト型などのオブジェクトを処理できますが、リスト型のsortメソッドはリスト型以外に使うとエラーになります。
print(len("hello")) # 文字数を返す
print(len([1, 2, 3])) # リストの要素数を返す
a = "hello"
a.sort()
# エラー! str型に sortメソッドは存在しない
おわりに
本記事では関数とメソッドの違いについて解説してきました。
「呼び出し構文の違い」という基本から、それぞれの定義に踏み込んだ本質的な違いまで触れました。
ここを理解できていれば、後のクラスやオブジェクト指向、といった初学者の多くがつまづく部分も理解しやすくなってきます。
一度で理解できなくても、何度か眺めて徐々に浸透させていってください!