2012.07.21
Pythonの「@staticmethod」はどのように役立つのか
Peterbe.com - Newfound love of @staticmethod in Python
http://www.peterbe.com/plog/newfound-love-of-staticmethod

Pythonのデコレータ「@staticmethod」はどのように役立つのか、かんたんな例で解説している。

class Printer(object):

  def __init__(self, text):
    self.text = text

  @staticmethod
  def newlines(s):
    return s.replace('\n','\r')

  def printer(self):
    return self.newlines(self.text)

p = Printer('\n\r')
assert p.printer() == '\r\r'

ここで「@staticmethod」がついている、メソッド「newlines」がstaticmethod。Pythonの場合、メソッドの最初の引数は通常「self」(このクラスのインスタンスを示す)だが、staticmethodではこれがない。つまり、クラスの中で定義するけれども、インスタンスとはやりとりしないのがstaticmethodだ。

インスタンスとやりとりしないのなら、メソッドでなく、ただの関数でいいのではないか。

def newlines(s):
  return s.replace('\n','\r')

class Printer(object):

  def __init__(self, text):
    self.text = text

  def printer(self):
    return newlines(self.text)

p = Printer('\n\r')
assert p.printer() == '\r\r'

こんなふうに、「newlines」をただの関数にすれば十分ではないのか。もちろん、これでもいい。多くの場合、これで足りる。

ではなぜ、staticmethodなるものがあるのか。staticmethodは、「ただの関数」を通常のモジュールレベルではなく、クラスレベルで定義したものと言える。「ただの関数」をクラスレベルで定義して、なんの意味があるのかというと、例えばこれを継承して、サブクラスでオーバーライドできる。

class UNIXPrinter(Printer):

  @staticmethod
  def newlines(s):
    return s.replace('\n\r', '\n')

p = UNIXPrinter('\n\r')
assert p.printer() == '\n'

これと同じことを、モジュールレベルの「ただの関数」でやろうとすると、なかなかできない。

このようにサブクラスでオーバーライドしたい場合、通常は、ただのメソッド(最初の引数がself)を使うだろう。もちろんそれでもいい。しかし、そのメソッドがインスタンスとやりとりしないのであれば、「@staticmethod」のほうが、そのメソッドの意味・役割がより明確になる。

ちなみに、ブログ主のPeter Bengtssonは、いまはMozillaで働いているPythonエンジニアで、以前はZopeをやっていた。古いZopeユーザなら「QuickLinks」というプロダクトを覚えているかもしれないが、その作者である。


関連エントリ:
Pythonで関数プログラミング入門
http://mojix.org/2009/04/26/python_functional
Pythonを使って、1行でファイル共有Webサーバを立ち上げる
http://mojix.org/2009/03/05/python_one_line_fileserver