Address
304 North Cardinal St.
Dorchester Center, MA 02124

Work Hours
Monday to Friday: 7AM - 7PM
Weekend: 10AM - 5PM

【Python入門】スタティックメソッド(@staticmethod)とは?その基本とメリットについて解説

Pythonの@staticmethod(スタティックメソッド)についてのその基本的な考え方と使い方の解説

今回は、Pythonのクラス内部のメソッドについて使われる、スタティックメソッド(@staticmethod)の基本についての解説です。

プログラミング言語の人気オンラインコース

前提知識:デコレータとは?

まず@staticmethodはPythonのデコレータの一種です。したがってその理解については、デコレータの知識が重要です。当ブログでもデコレータについては基本を解説していますので、次の記事をご覧ください。

スタティックメソッドの機能・メリット

機能

乱暴に言ってしまえば、スタティックメソッドとは次のような機能を持つものです。それは、

  • スタティックメソッドが使われているクラス
  • そのスタティックメソッドに変化した関数

この2つを分離し、それらの関係性を切断する機能です。

メリット

スタティックメソッドを使う場合のメリットはいくつあると思われますが、その一部としては、

  • スタティックメソッドの対象となった関数を、クラスとは独立して単体でテストしやすい
  • スタティックメソッドの対象となった関数は、クラスやインスタンスにはアクセスできないため、スタティックメソッドの関数をいじっても、クラスには影響を及ぼさない(もしクラスが間違って変更されてしまうと、そこから生成される様々なインスタンスやそのクラスを継承するクラスにも影響を及ぼすため影響が大きくなり、コード全体が破綻する可能性がある)

というところでしょうか。

メソッドと関数の違いとは?

使い方における見た目上の違い

上述のようにスタティックメソッドの機能とは、メソッド(クラス内部で定義される関数のこと)を普通の関数として使えるようするものでした。逆にいえば、メソッドは普通の関数として使えない(使いにくい)ということです。それは一体なぜなのでしょう?

まずそれぞれの形式的な使い方の違いをみてみましょう。通常の場合、メソッドは次のように使うでしょう。

class C:
    def fnc_c(*arg, **kwarg):
       (略)

c = C():
c.fnc_c(引数)

一方で普通の関数の使い方は、

def fnc_d(*arg, **kwarg):
   (略)

fnc_d(引数)

このように形式的には使い方の見た目が普通は次のように異なります。

# メソッドの普通の使い方
インスタンス.メソッド名(引数)

# 関数の普通の使い方
関数名(引数)

実質的な違い

もう少し実質的な違いを考えてみると、それは第1引数に何をとるのかという点で異なります

まずメソッドの場合ですが、実はメソッドとして使うときは自動的に第1引数にそのクラスが挿入されます。たとえば、

class C:
    def fnc_c(x)  
        return x

c = C():
c.fnc_c("Tom")

これを実行すると、

Traceback (most recent call last):  File "<stdin>", line 1, in <module>
TypeError: fnc_c() takes 1 positional argument but 2 were given

とエラーがでます。なぜなら、メソッドfnc_cの第1引数xには、クラスCから生成されたインスタンスオブジェクトのc(究極的にはクラスC)が自動的に設定されるからです。

結果、メソッドfnc_cは「fnc_c(c, “Tom”)という感じで2つの引数(cと文字列Tom)を持つこととなり、それはメソッドとして定義した仮引数の数(今回はxただ1つ)と一致しません。したがってエラーが出ます(この点、詳しいことは公式ドキュメントの、__self__属性や__func__属性の説明に譲ります)。

仮に次のようにすると、エラーは出なくなります。

class C:
    def fnc_c(self, x):
        return (self, x)
    
c = C()
c.fnc_c("Tom")

メソッドを定義している部分では仮引数が2つ、しかし、実際に使うときの実引数は1つです。このように見た目と実際の使用時とで引数の数が異なります。(なおメソッドの第1引数の名前はselfとするのが一般的ルール?となっています)

このように、通常のメソッドはそれが属するクラスの情報を引き継ぐため、クラス内部の様々なデータを利用・編集できますが、逆にいえば通常のメソッドの動きがおかしくなればクラスにも悪影響を及ぼす可能性があります。

対照的にスタティックメソッドはそれが属するクラスとは独立の存在となりますので、クラス内部の様々なデータにはアクセスできません。しかしこれはスタティックメソッドの関数がおかしくなってもクラスには影響を与えないという利点にもなります。

スタティックメソッドの使い方

では実際のスタティックメソッドの使い方を簡単に見てみましょう。

class C:
    @staticmethod
    def fnc_d(x, y):
        print(x, y)
                
# クラスインタンスオブジェクト作成    
c = C()
# 普通のメソッドの使い方:クラスインスタンスオブジェクト対して、そのメソッドを呼び出す。バウンドメソッドオブジェクト方式
c.fnc_d(2,3)  
C().fnc_d(2,3) 

# スタティックメソッドの使い方:クラスに対して、メソッドを呼び出す感じ。アンバウンドメソッドオブジェクト方式
C.fnc_d(2,3)

結果は全て、

2 3

という出力となります。上で説明した通常のメソッドの場合と異なり、メソッドfnc_dの第1引数xに、クラスインスタンスオブジェクトが設定されていないことがわかると思います。

staticmethodを使う場合には、

self.XXXX = ◯◯◯

このようなself(selfにはクラスインスタンスオブジェクトが入る)を使ったアクセスができないため、クラスやインスタンスの情報が意図せずに書き換えられるということが防げます。

これがクラスとメソッド(スタティックメソッドが設定されているメソッド)の関係性を断ち切っているという意味です。

最終行の、

C.fnc_d(2,3)

この部分ですが、クラスCであって、インスタンスオブジェクトの「c」ではない点に着目してください。スタティックメソッドはインスタンスを作る必要がないのですね。

なお、デコーレータとしての「@staticmehtod」を使わない書き方も可能で、その場合は組み込み関数の「staticmethod()」を使い、

class C:
   def fnc_c(x, y):
        print("staticmethod", x, y)
   fnc_c = staticmethod(fnc_c)
  
C.fnc_c(2, 3)

としても結果は、

2 3

となります。なぜなら、

   fnc_c = staticmethod(fnc_c)

このコードにより、fnc_cはstaticmethodへと変化しており、その第1引数にクラスインスタンスオブジェクト(究極的にはクラス)が挿入されることがなくなるため、仮引数xには2、yには3がそのまま入るからです。

Python学習には動画学習がオススメです。特にUdemyの次のような人気講座がオススメです。

@classmethodと@propertyについて

スタティックメソッド(@staticmethod)以外にも、クラス内部で使われる有名なデコレータがあります。それが、クラスメソッド(@classmethod)とプロパティ(@property)です。これらについては以下の記事で解説を書いていますので、ぜひ読んで見てください。

愛を分かち合いましょう