関数とメソッドの違い 【Python】

programming Python

Python の関数(function)とメソッド(method)の違いについて、実際に動くスクリプトの形でまとめ、ポイントを整理しておきます。
以下の環境で動作確認をしています。
環境: Windows パソコン、Anaconda、Python 3.x

背景 ~ 関数とメソッドの違いは何か

Python でプログラミングをしていると、関数 funciton とメソッド method が出てきます。
似た概念、重複のある概念が、プログラミングを学ぶ最初の段階から出てくるため、明確に理解しないまま、Python を使っているといったケースも生じていると思います。

関数とメソッドの違いについてネット検索をすると、たとえ話などで説明されたサイトが多数ヒットします。
しかし、実際に目にする Python のスクリプトと、書かれている説明とが結びつかず、やはり混乱することが多いと思います。

理解するには、けっきょく、実際にプログラムを動かしてみるのが最短です。
ということで、関数 function とメソッド method について、できるだけシンプルにスクリプトとポイントをまとめ、公開しておくことにします。

サンプルコード:関数とメソッドの違い

以下のスクリプトをまずは、動かしてみてください。
# function としたところが、関数 function です。
# method としたところが、メソッド method です。

def function_circle1( radius1 ):                 # function 
   pi1 = 3.1415926 
   val1 = pi1 * ( radius1 ** 2 ) 
   return val1 

class Class_Calculate1:                          # class 
    def __init__( self ): 
        self.instance_variable1 = 3.1415926      # instance variable 
    def method_circle1( self, radius1 ):         # method 
        val1 = self.instance_variable1 * ( radius1 ** 2 ) 
        return val1 

# function 

print( "function " ) 
print( function_circle1( 10.0 ) )                # >> 314.15926 
print( "" ) 

print( 'len( "abc" )' )                          # function 
print( len( "abc" ) )                            # >> 3 
print( "" ) 


# method 

fm1 = Class_Calculate1() 
print( "method:1" ) 
print( fm1.method_circle1( 10.0 ) )              # >> 314.15926 
print( "" ) 

fm1.instance_variable1 = 3
print( "method:2 " ) 
print( fm1.method_circle1( 10 ) )                # >> 300 
print( "" ) 

print( '"a,b".split(",")' )                      # method 
print( "a,b".split( "," ) )                      # >> ['a', 'b'] 

関数とメソッドの違いについて、一般的にいわれている主なポイントをスクリプトの形で集約すると、上記の程度になります。
この30行程度のスクリプトをざっと見て、プログラムの動きや相違が理解できるのであれば、特に問題はないのではないかと思います。

サンプルコードの説明

関数 function の例

・ サンプルコードで、最初に def function_circle1() としているところが、関数 function を定義する一例です。
・ スクリプトの見た目のまま、def で定義されていたら、関数 function という理解でよいです。視覚的に覚えてしまえばよいです。
・ def で関数を定義したら、実行時は、直接、関数 function_circle1() を単独で呼び出して実行できます。メソッドのように、ややこしい手順(クラス class を定義して、インスタンスを生成する)は不要です。
・ サンプルコードでは、円の面積を求める関数 function_circle1() を定義したので、この関数を呼び出して半径 10 の円の面積を求めると、314.15926 と計算されます。
・ Python 標準の関数(function、組み込み関数)としては、print()、len()、round() などがあります。標準関数は、すでに def で定義されていると理解すればよいです。
これらはいずれも、単独でそのまま使用できます。
・ プログラムが動いたら、この関数の部分をコピー&ペーストしてカスタマイズして(独自の関数を定義して)、動かしてみてください。

メソッド method の例

・ 一方、クラス(Class_Calculate1)を定義し、その中で def method_circle1() としているところが メソッド method を定義している部分です。
・ スクリプトの見た目のまま、
① クラス class の中で定義した関数 function がメソッド method
という理解でよいです。ややこしいほうが、メソッド method です。これも、視覚的に覚えてしまえばよいです。
・ メソッドを使う場合、
② まず、クラス(Class_Calculate1)を呼び出してインスタンス(instance1)を作ります。
③ つぎに、[インスタンス名] + [ドット “.”] + [メソッド] として使いたいメソッド(関数)を呼び出します。
メソッドの前には、かならず、ドット “.” がくる、という理解でよいです。
いいかえると、メソッド method_circle1() は、単独では呼び出すことができません。かならず、インスタンスに紐づけて(インスタンスにドット “.” でつなげて)、呼び出します。
これに対し、クラス内で定義されていない、一般の関数 function_circle1() は、単独で呼び出すことが可能です。
メソッドは、インスタンスに紐づけないと呼び出せないよう制限をかけた関数というとらえ方ができます。制限をかけることで、意図しない使用をなくす/バグが生じる潜在的な可能性を減らすことができます。

・ 続けて、上記のサンプルコードでは、メソッドに加えて、インスタンス変数を呼び出す例についても記載しています。
インスタンス変数 instance_variable1 も、メソッドと同様に、[インスタンス名] + [ドット “.”] + [変数名] とすることで、呼び出し、書き換え等ができます。
メソッドと同様、インスタンスにぶら下げたインスタンス変数を、ドット “.” でつなげて呼び出してくる、といった理解・感覚でよいと思います。

用語の整合性という観点でみると、もし、クラスで定義した変数について「インスタンス変数」という用語を使うのであれば、「メソッド」という用語は使わずに「インスタンス関数」という用語を使えば、整合性が明確化できてよいのにと思います。
「インスタンス変数」も、「インスタンス関数」(メソッド)も、インスタンスにぶら下げて定義されているという点で共通しているからです。

・ Python 標準のメソッド (method) としては、split() や join()、append() などがあります。いずれも、”.split()”、”.join()” 等として使用します。標準メソッドについても、メソッドの直前には、ドット “.” がくるという理解でよいと思います。

・ スクリプトの末尾では、標準メソッドの事例として、”a,b” という文字列を使った例を入れてあります。”a,b” のデータ型(文字列型)に紐づけられているメソッド .split() を呼び出しています。
この場合、明示的にはクラス class を定義していませんし、インスタンスを定義した行もありません。
しかし、文字列 “a,b” をスクリプト上で定義(実行)した時点で、プログラム実行中に(RAM 上に)文字列型のオブジェクトが生成されています。したがって、実質的にインスタンスを定義しているのと同等となって、その結果、文字列型のオブジェクトに紐づけたメソッドを使うことができる、ということになります。

標準関数と標準メソッドの見分け方

標準関数と標準メソッドの見分け方としては、原則、len( … ) のように、関数単独で呼び出し/実行ができるか、または、[…].[メソッド] のように、ドット “.” をつけて呼び出すか、で判別できます。

インスタンス(オブジェクト)は、どのように理解すればよいか

関数と比較すると、クラスやメソッドを使う際は、上記の「インスタンスを生成する」(②)というステップが加わるところが特徴的です。

まずは単純に、インスタンス instance の英単語の意味を確認すると、「例、実例」となっています。学生時代に暗記した、”for instance”、”for example” (例えば、具体的には)と同様の意味です。
単語の意味からすると、具体化するといったとき、「抽象的な何か」を意識していることになります。その「抽象的な何か」を具体化したものが「インスタンス」ということになります。
プログラミング言語の分野でインスタンスといったとき、「抽象的な何か」は「クラス」に対応すると考えられます。
したがって、インスタンスは、クラスに(抽象的に)書かれた内容を具体化したものであって、プログラムの実行中に、変数を書き換えたり、任意の処理が可能な具体的な実体という程度の理解でよいと思います。
言い換えると、クラスの場合は、いまだ具体化していない記載であるため、直接的に、変数を書き換えたり、任意の演算を実行するなどの具体的な処理を行うことができない、ということになります。

クラスから生成したインスタンスは、オブジェクトといわれることがあります。
オブジェクトといった場合は、”for instance” のような「何か」を具体化したといったニュアンスが失われています。
すると、クラスから生成したということを意識して言及する場合はインスタンスという表現を使い、クラスを意識していない場合は、オブジェクトいう表現を使う、といった程度の認識でよいと思われます。

私は、クラスやメソッドの概念を勉強しはじめたころ、いったんクラスを定義(①)した後、「インスタンスを生成する」(②)というステップを入れる必要性が理解しづらいと思いました。
クラスをすでに定義しているのに、なぜ、さらに、インスタンスを生成する必要があるのか、というところです。一般の関数の場合、最初にどこか1か所で定義をしておけば、その後は自由に使えます。関数に比べると、クラスやメソッドは、無駄なことをやっているように見える、二度手間に見えるからです。

しかし、ハードディスク(不揮発メモリ)上に書かれているスクリプト(クラス)に対し、インスタンスを定義する命令を実行することで、インスタンスに対応したメモリ領域が揮発性メモリ RAM 上に確保、維持される、と理解したところ、自分なりに納得ができました。
スクリプトの前半でクラスの定義さえ書いておけば、あとはインスタンスを何個でも生成・維持できるからです。インスタンスを2個定義すると、2個分の領域が RAM 上に個別に確保される、ということです。
たとえば、ゲームを作っていて、人間のクラスを1つ定義しておけば、いくつかの行でインスタンスを生成させるスクリプトを書いておくことで、プログラムの実行中、個性の異なるキャラクターを何人でも生成し、プログラム実行中(プログラムを終了するまで、メモリ上で)、維持できる、ということになります。
関数の場合は、関数を呼び出して戻り値を取得した時点で、関数の実体が RAM 上から解放されてしまいます。戻り値は数値や文字列として維持はできますが、関数自体の実体は、メモリ上から解放されてしまいます。
メソッド(関数)や変数(インスタンス変数)のように、関連する機能やデータ一式(インスタンスに相当するもの)を一つにまとめて維持することが難しくなります。
プログラムの実行中にインスタンス(オブジェクト)を生成することにしてしまって、関連するパラメータや機能をインスタンスに詰め込んでしまい、プログラムが終了するまで状態を維持しておきたくなる、という需要、必要性が理解できます。

プログラムを書く際の感覚を書き出しておきます。
メソッドはかならずインスタンスにぶら下げて定義したはず/定義されているはずです。そこで、インスタンスにドット “.” をつなげて、そのインスタンスの中身を呼び出してくる、といった感覚で、スクリプトを書くことになります。
クラスの定義は原則自由です。すると、同一名称で処理内容の異なるメソッドが任意のクラス内で定義できてしまいます。
すると、どのインスタンス(どのクラス)にぶら下げたメソッドを実行するのかを特定しないと処理内容が特定できません。そこで、処理内容を明示するためには、インスタンスをドット “.” で繋げるしかないではないか、といった感覚でプログラムを書くことになります。

Python の仕様を確認しておく

つぎに、Python の仕様を確認しておきます。
ライセンスの都合上、原文をそのまま転記するのは避けて記載します。

関数 function の定義

・ https://docs.python.org/3/glossary.html#term-function

関数 function の定義は、「呼び出し側 caller に対して何らかの値を返す、ステートメントの集まり a series of statements」(★1)といった程度の記載になっています。
他の言語などでも使われている、いわゆる “関数” と同等の理解でよいと思われます。

メソッド method の定義

・ https://docs.python.org/3/glossary.html#term-method

一方、メソッド method の定義は、「クラス class の中で定義された関数 function のこと」(★2)といった程度の記載になっています。
この記載によれば、けっきょくのところ、関数とメソッドの違いは、クラス内で定義されたか否か程度の違いしかありません。
また、この記載からは、何のためにこんなことをやっているのか、理由が明確に書かれた部分が見つかりません。

したがって、Python の言語仕様に忠実に従って説明されている文献やサイトを読めば読むほど、なぜこういった仕様にしたのかといった理由についてはわからなくなると思います。
言語仕様の原文を確認すると理由については記載されておらず、「Python ではそういう言語仕様にしたのだからそうなっている」という程度の説明しかしようがないからです。(わからないのは当たり前です。。)

なお、メソッド method は、クラス内で定義された関数 function、と定義されています。したがって、関数 function のほうが広い概念であって、メソッド method は、関数 function に含まれる(★3)、ということになります。

少し考察 ~ なぜ理解しにくいか、を理解する

話が脱線しますが、関数とメソッドの違いについて、なぜわかりづらいのか考察をしておくことにします。

用語(関数、メソッド)を明確に使い分けていない説明が多い

Python のプログラミングについてネット検索すると、関数とメソッドの違いをあまり意識せず説明したサイトが多いです。
たとえば、「len() は文字数を調べるメソッドです」とした説明があります。オブジェクトに依存せず単独で実行できるため、「関数」としたほうがより正確かなと思います。
こういった説明でメソッド method を理解してしまうと、その後、クラス class とメソッド method が出てきたとき、混乱します。
クラス class でのメソッドの定義の仕方が、それまで出てきていた関数の定義の仕方とは異なっており、呼び出し方、使い方も異なっているためです。

「含む、含まれるの関係」になっている

また、「メソッド method は、クラス内で定義された関数 function 」(★2)となっており、数学の集合でいうと、含む、含まれるの関係になっています。
関数のうちの一部がメソッドです。上位概念、下位概念の関係になっているわけです。

したがって、「関数」と「メソッド」を比較すること自体、論理的な混乱を引き起こしやすいことになります。
「リンゴ」と「バナナ」を比較するならともかく、「果物」(関数)と「リンゴ」(メソッド)を比較しているようなものだからです。

Python の言語仕様によれば、クラスで定義された「関数」を「メソッド」というのは問題ありません。定義どおりです。
しかし、クラスで定義されていない一般の「関数」のことを「メソッド」といってしまうと、間違いとなります。
一方、「メソッド」のことを「関数」といっても、間違いではありません。Python の言語仕様では、メソッドはクラス内で定義した「関数」と書いてあるからです。

この定義によれば、説明する側が、メソッドのつもりで「関数」と発言しても、とくに間違いではありません。
ところが、その説明を聞いた側がクラスで定義されていない「関数」のことも指すのかと理解すると間違ってしまうことになります(なんと!)。

プログラミングを教えている先生よりも、習っている生徒の「言語能力」のほうが高い場合(先生がアバウトに教えている場合)、先生は間違った発言はしていないとしても、聞いた側には間違って伝わりうることになります。
言語能力の高い人ほど混乱が生じうることになります。(言語能力の低い人は矛盾や混乱を認識しないケースが多い、天下り式にとにかく覚えろ、まず慣れろと指示しやすい。。)

混乱を招きやすい一例として、関数とメソッドの違いについてネット検索をしてみると、「関数は単独で実行できる、呼び出せる」(★4)といった旨の解説が出てきます。
しかし、メソッドはクラスで定義された関数(★2)となっていますので、関数の一種であるメソッドも単独で実行できると解釈できる(!)ことになってしまいます。
関数とメソッドの「違い」について知りたかったはずなのに、関数はメソッドを含んでしまっているため、循環論法のような説明をされることになります。(★4)は不正確ということになります。

修正をするなら、「クラスで定義されていない関数は単独で実行できる」とするか、「メソッドは単独では実行できない」が、より正しいと考えられます。(「メソッドは単独では実行できない」が真であるとしたとき、ここから「関数は単独で実行できる」という結論を導き出してしまうと、論理的な誤りとなります。対偶を取っていませんので。。)
とはいえ、関数やメソッドがわからないといっている人に対して関数を説明するとき、「クラス」や「メソッド」という用語が出てきてしまう、というのも考えものです。(循環論法のような説明になってしまう。定義上、関数がメソッドを含んでしまっているため、関数を説明しようとすると、メソッドが出てきてしまう。関数は def で定義したもの、程度に抑えておくのが無難と思われます。わかりやすさのために説明を単純化しようとすると、循環論法のような説明に陥ってしまう。。)

(★1)と(★2)が、含む、含まれるの関係になっているため(≒正確な記載なのですが、結果的に悪文となっているため)、この種の混乱が多発することになります。
(このサイトの説明でも、文脈に応じて「関数」≒「クラスで定義されていない関数」の意味で記載することにします。そうでないと説明が冗長になりすぎるため、また、トートロジーのような説明になってしまうためです。。)

機能としては重複するのに、関数とメソッドの2つの概念がなぜ必要なのか、明確に書かれた文献が少ない

また、Python の言語仕様に沿って、天下り式に関数 function やメソッド method の使い方を説明した文献やサイトが多いと思います。
すると、最初に関数 function が出てきて、作りたい機能はすでに関数で書くことができているのに、それとほとんど同等の機能をわざわざクラス class とメソッド method を使って再び書いてみる、といった流れになります。

プログラミング言語を学ぶ側としては、クラスやメソッドがなぜ必要なのか理由もよくわからないまま、クラスの使い方をまず覚える。覚えて丸飲みしたあと、それらの必要性を理解する、といった流れになってしまいがちです。

実際のところ、クラス class やメソッド method といったややこしい内容を理解しなくても、関数 function の理解ができた段階で、ほとんどの作りたい機能を関数のみを使ってプログラミングすることができてしまうと思います。
また逆に、クラスやメソッドのみを使ってプログラミングをすることも可能だと思います。
すると、学び始めの段階で、同じ機能を実現するのに2通りの書き方ができるのはなぜかと素朴に疑問を感じたままになってしまう、腹に落ちない状態が続いてしまう、と思います。
加えて、説明をする側も、同一機能を実装するとき、関数を使う派と、メソッドを使う派にわかれてしまいます。混乱が生じる一因になると思います。

クラスやメソッドを使う理由としては、関数を使ってもよいけれども、複雑なプログラムを作るとき、クラスやメソッドをうまく使うと、実現したい機能をまとめてしまうことができます。すると、余計なコードを書かずに済むことになる。また、グローバル変数も減らすことができる。すると、バグが入り込む余地をなくす/減らすことができる、といったところでしょうか。
あるいは、関数をそのまま定義してしまうと、どこからでも呼び出せるグローバルな関数になってしまう。するとプログラムが大きくなるほど複雑度が上がり、思わぬ使い方をされる可能性が高まる。(バグが生じる可能性が高まる。プログラムが大きくなるほど、関数や変数が予想外の使い方をされていないことを確認、検証するコストが急増していく。デバッグのコストが急増する。)
そこで、関数をクラスの中に移行して、単独では実行できない(オブジェクト(インスタンス)からだけ呼び出せる)よう制限をかける。これにより、バグが生じる可能性や検証のコストを最小化する、といったところでしょうか。

Python の場合、クラスを使いたくない場合は関数だけを使ってプログラミングができるようにし、また、クラス(とメソッド)を使ったプログラミングもできるようにした、と考えられます。
プログラミングを学習する初段階では、こういったあたりの関係性が把握しにくいと思います。

少し見方を変えると、なぜこのような混乱が生じうる言語仕様にしたのか、まったくわかっていなかったとしても、言語仕様に基づいて忠実にプログラムを書きさえすれば、実際には意図したとおりに動くので、何ら問題はない、問題は何も起きない、ということもできます。

混乱する一因となっていると思われる仕様 ~ import

加えて、Python のメソッドに関し、混乱する一因となっていると思われるものとして、import の仕様があります。
例えば、スクリプトの冒頭で import os とし、この後、path1 =os.path.dirname(__file_) 等として、パスを取得するような場合です。
この場合、”os.path.…” の “.path” 以下は、ドット “.” を使ってはいるものの、メソッドではなく関数です。
import os 自体はインスタンスを定義しているわけではなく、他のモジュールを呼び出しているだけ(*.py で書かれたテキストを読み込んでいるだけ)だからです。

この例では、関数 os.path.dirname( … ) の部分は、Python のプログラミングに慣れることで、関数 len( … ) と同じように見えていないといけない、ということです。
もし、関数 os.path.dirname( … ) の部分が、メソッド “a,b”.split( “,” ) と同じように見えていたら、正確に理解できていない、混乱が生じている、ということになります。

Python では、「メソッド」を呼び出す際に “.” を使います。一方、これとは別に、他のモジュール(*.py ファイル)で定義された「関数」を呼び出す際にも “.” を使う(!)、という紛らわしい仕様になっています。
こういったあたりも、Python のプログラミング言語をひととおり覚えた後でないと、明確には認識しづらいため、混乱する一因になっていると思います。

他のプログラミング言語との比較

参考までに、関数とメソッドについて複数のプログラミング言語を比較して、まとめてみることにします。
いくつかの言語を知っていると、関数とメソッドの違いがより的確に理解できると思います。

・ C言語では、関数 function のみが使用できる。(構造体を使うことができる)
・ C# と Java では、メソッド method のみが使用できる。(クラスをかならず使う)
・ C++、JavaScript、PHP、Python では、関数、メソッドのいずれも使用できる。(クラスを使うか否かは任意)
・ Ruby は、すべてのデータがオブジェクトであるため、関数とメソッドの区分けはなく、メソッドの概念で統一されている。しかし、クラスの内外でメソッドを定義できるため、Python と同様の使い方ができる。(クラスを使うか否かは任意)

・ C言語は、関数だけしか使えません。クラスの定義はできませんが、類似するものとして、構造体があります。構造体を定義することで、変数をまとめることまでが可能です。C言語でいう構造体を、関数も定義できるよう発展させたものがクラスだと理解することができます。
・ C#、Java では、関数はクラス内で定義するようになっています。実質的にメソッドのみが使えるようになっています。
・ C++、JavaScript、PHP、Python、Ruby は、クラスを使っても、使わなくても、プログラムを作ることができるようになっています。実質的に、Python でいう、関数とメソッドのいずれもが使えるようになっています。

まとめ

メソッド method と関数 function の違いについて、サンプルコードとポイントをまとめました。

Python を学び始めたころ、ネット検索をしたのですが、プログラムの動作に即した形で違いを端的に説明しているサイトや書籍をあまり見かけませんでした。一方、Python の言語仕様を確認すると、これ以上、明確にはできないと思うほどシンプルな定義になっています。
ところが、なぜわかりにくいのか理解しようとすると、明確に解説されたサイトや書籍がほとんど見つかりません。なぜ、説明が循環論法になっているということをズバリ指摘した書籍やサイトが見つからないのでしょうか。。

ということで、「こう説明してもらえていたら、時間を無駄にせずに済んだのに」と思っていた部分について言語化し、公開しておくことにします。

関連リンク
・ クラスのポイントまとめ 【Python】
・ 「参照」に関するポイントまとめ 【Python】
・ 他のスクリプトの関数を実行する
・ Python で他の Python スクリプトごと実行する方法
・ Python で四捨五入をするときのまとめ
・ おすすめ書籍 ピックアップ 【機械学習】
・ モジュール、パッケージ、ライブラリのまとめ【Python】

外部リンク
・ Python の組み込み関数

タイトルとURLをコピーしました