試験運用中なLinux備忘録・旧記事

はてなダイアリーで公開していた2007年5月-2015年3月の記事を保存しています。

Pythonにおけるクラスの静的メンバについて

はじめに

以前「オブジェクト指向についての覚え書きとPython上のクラスと組み込みオブジェクトについて」という記事も書いている通り、オブジェクト指向やその中のクラス設計などについてはまだよく分かっていない部分がかなり多く、本記事においても誤りが含まれている可能性はある。
そのような中でクラスの「静的メンバ」というものについて幾つか分かったことがあるので、例とともにここにまとめておくことにする。

概要

静的メンバ変数(静的属性/静的attribute)

クラスの中にあるメンバ変数は、多くの場合

class ClassName:
  [以下メンバの定義...]

のようにして定義したクラスに対して

obj = ClassName()

のようにして生成した各オブジェクト(インスタンス)に属するデータ*1を持っていることになる。しかし、Python以外の幾つかの言語では「static」を付けてメンバ変数を宣言することで「各オブジェクトには属せず、そのクラスの全てのオブジェクトに共有されていて、クラス自体に関連付けられた形のメンバ変数」が作れる。これは

[クラス名].[メンバ変数名]

として読み書きする。
Pythonのクラスでは「static」などと付けて特別に静的メンバ変数とするようなことはできない(staticと非staticの区別がない)が、上に書いた形式でデータを操作することができる。
なお、用途としては上にも書いたように全てのオブジェクトから共有されるようなデータなどが考えられ、定数にも使えそう。

静的メンバ関数(静的操作/静的method)

静的メンバ変数が各オブジェクトに属しないデータなら静的メンバ関数は各オブジェクトの持つデータとは関係を持たない関数ということになるが、分類・配置上の関係でそのクラスに入れておくのが好ましいというケースは考えられる。
そういったときには静的メンバ関数として定義することになるのだが、Pythonではクラスに属するメンバ関数への1番目の引数(多くは「self」と書かれ、そのクラスのインスタンスを指定する)が必要ということでそのままでは処理できない。
そこで特別にメンバ関数の定義(「def」の行のすぐ上)に

@staticmethod

を付ける。*2
メンバ関数の定義部分では最初の引数(例の「self」の引数)を付けずに記述する。
なお、静的メンバ関数Python以外の幾つかの言語では「public(公開)*3」と「static(静的)」の修飾子が付いているようだ。

[任意]ファイル名: staticmembertest.py

#! /usr/bin/python
# -*- encoding: utf-8 -*-


class TestClass:
  static_cnt = 0  # これを静的メンバ変数として使用する
  def __init__(self, str):
    # 静的メンバ変数として使用したいメンバ変数の操作は[クラス名].[メンバ変数名]
    TestClass.static_cnt += 1  # コンストラクタが呼ばれた数を保持するものとする
    #self.static_cnt += 1      # 左だと各オブジェクトの持つ値を操作してしまう
    print 'TestClass(): static_cnt=%(cnt)s str=%(str)s' \
          % {'cnt' : self.static_cnt, 'str' : str}
  def get_obj_count(self):
    """
    本クラスから作成された全てのオブジェクトが共有している値static_cntを返す
    """
    return self.static_cnt
  @staticmethod   # 静的メンバ関数に付ける関数デコレータ(Python 2.4以上)
  def max(a, b):  # 静的メンバ関数にはself(インスタンスオブジェクト)を付けない
    """
    オブジェクトのデータと無関係だがクラスに属する関数のテスト
    2つの数のうち大きいほうを返す
    """
    return b if a < b else a  # Python 2.5以上でないと使えない書き方


tst1 = TestClass('aaa')
tst2 = TestClass('bbb')
tst3 = TestClass('ccc')

# 静的メンバ変数static_cntを参照
# 全てのオブジェクトが同じ値を共有しているのが分かる
print 'tst1.get_obj_count(): %d' % tst1.get_obj_count()
print 'tst2.get_obj_count(): %d' % tst2.get_obj_count()
print 'tst3.get_obj_count(): %d' % tst3.get_obj_count()
# 静的メンバ関数を呼び出す
print 'TestClass.max(3, 5): %d' % TestClass.max(3, 5)
print 'TestClass.max(6, 6): %d' % TestClass.max(6, 6)
print 'TestClass.max(15, 7): %d' % TestClass.max(15, 7)

下は実行結果。中盤でメンバ変数static_cntが全てのオブジェクトから同じ値として取り出されていることが分かる。最後では静的メンバ関数へ2つの(オブジェクトと関係のない)整数を渡して処理結果を受け取っている。

TestClass(): static_cnt=1 str=aaa
TestClass(): static_cnt=2 str=bbb
TestClass(): static_cnt=3 str=ccc
tst1.get_obj_count(): 3
tst2.get_obj_count(): 3
tst3.get_obj_count(): 3
TestClass.max(3, 5): 5
TestClass.max(6, 6): 6
TestClass.max(15, 7): 15

関連記事:

参考URL:

使用したバージョン:

*1:つまり、各インスタンスがそれぞれ持っている個別のデータ

*2:この書き方はPython 2.4以上を要求するが、組み込み関数staticmethod()を使用する場合はバージョン2.2からでOKらしい

*3:外部から直接参照できる