クラスの定義の基本からさまざまな属性の定義、クラスの継承や多重継承まで、クラスに関わるさまざまな構文をギュッとまとめた。
この記事は会員限定です。会員登録(無料)すると全てご覧いただけます。
今回はクラスの定義の基本構文、クラス変数/クラスメソッド/スタティックメソッドの定義、プライベートな属性、プロパティ、クラスの継承、多重継承の構文をまとめる。
なお、クラス定義にまつわる詳細については「Python入門」の以下の記事などを参照されたい。
クラス定義の基本を以下に示す。
class クラス名:
def __init__(self, パラメーターリスト):
インスタンスの初期化処理
def インスタンスメソッド名(self, パラメーターリスト):
インスタンスメソッドのコード
このようにして定義したクラスはobjectクラスの派生クラスとなる。
__init__メソッドは、そのクラスのインスタンス(オブジェクト)を定義する際に、インスタンスが持つ属性(インスタンス変数)の初期化などを行うために使用する。インスタンスメソッドは、インスタンスを介して呼び出し可能な操作である。
これらのメソッドの定義では第1パラメーターには暗黙の「self」を置く(selfには初期化を行う対象となるインスタンス、またはメソッド呼び出しに使われたインスタンスが渡される)。メソッド内でインスタンスの属性にアクセスする際には「self.属性」の形でアクセスする。また、(一般には)メソッドを呼び出す際にはselfには引数の形で値を与えることはない。
クラスを定義すると、「クラスオブジェクト」が作成される。そのクラスのインスタンスを定義するにはクラス名にかっこ「()」を付加して呼び出しを行う(クラスオブジェクトは「呼び出し可能なオブジェクト」であり、かっこを付加することで呼び出し可能。この結果、__init__メソッドが呼び出されて、インスタンスの初期化が行われる)。
以下にクラス定義の例を示す。
class Foo:
def __init__(self, name):
self.name = name # インスタンス変数nameの初期化
def print_name(self):
print('name:', self.name) # 「self.name」として自身の属性にアクセス
f = Foo('foo') # クラス名にかっこ「()」を付加して呼び出して、インスタンスを定義
f.print_name() # インスタンスメソッドの呼び出し
Foo.print_name(f) # 通常はしないが、このような呼び出し方も可能
print(issubclass(Foo, object)) # True
Pythonのクラスでは、インスタンス変数/インスタンスメソッド以外に以下を属性として持たせることができる。
インスタンス変数とクラス変数の違いを以下の表に示す。
変数の種類 | 説明 | クラス外部からのアクセス方法 | クラス内でのアクセス方法 |
---|---|---|---|
インスタンス変数 | インスタンスが持つデータを保存 | インスタンス.インスタンス変数 | self.インスタンス変数 |
クラス変数 | クラスが持つデータや複数のインスタンスで共有するデータを保存 | クラス.クラス変数 インスタンス.クラス変数 |
self.クラス変数(値の書き換えに注意) クラス.クラス変数 self.__class__.クラス変数 type(self).クラス変数 |
インスタンス変数とクラス変数 |
クラス変数はクラスが持つ変数で、その値は全てのインスタンスで共有される。クラス変数はクラス定義内に「クラス変数名 = 初期値」として定義できる。クラス変数には「クラス.クラス変数」「インスタンス.クラス変数」などとしてアクセスできる。ただし、「インスタンス.クラス変数 = ……」として代入を行うと、クラス変数ではなく、インスタンス内にクラス変数と同じ名前のインスタンス変数が定義されるので注意が必要だ。
インスタンスメソッド、クラスメソッド、スタティックメソッドの違いを以下に示す。
メソッドの種類 | 説明 | 第1パラメーター | 呼び出し方 |
---|---|---|---|
インスタンスメソッド | インスタンスのデータを使って何らかの処理を行う | 呼び出しに使ったインスタンスがselfにセットされる | インスタンス.インスタンスメソッド(引数) クラス.インスタンスメソッド(インスタンス, 引数) |
クラスメソッド | クラスに関連した処理を行う | 呼び出しに使ったクラスがclsにセットされる | クラス.クラスメソッド(引数) インスタンス.クラスメソッド(引数) |
スタティックメソッド | クラスともインスタンスとも関連のない処理を行う | 特定のクラスやインスタンスはセットされない | クラス.スタティックメソッド(引数) インスタンス.スタティックメソッド(引数) |
インスタンスメソッド/クラスメソッド/スタティックメソッド |
クラスメソッドは、クラスに結び付けられたメソッドで、呼び出しには「クラス名.クラスメソッド名()」あるいは「インスタンス変数.クラスメソッド名()」の構文を使う。クラスメソッドの定義では第1パラメーターには(暗黙の)「cls」を置く(clsにはクラス自体が渡される)。インスタンス変数の属性にはアクセスできず、クラス自身を介した何らかの操作を行いたいときに定義する。クラスメソッドの定義では、一般にメソッド定義を「@classmethod」デコレーターで修飾する。
スタティックメソッドは、インスタンスともクラスとも結び付いていないメソッド。スタティックメソッドは「クラスを名前空間として、そこで定義された関数(メソッド)」と考えられる。その呼び出しは「クラス名.スタティックメソッド名()」または「インスタンス変数.スタティックメソッド名()」という形式で行う。インスタンスメソッドやクラスメソッドとは異なり、メソッド定義では暗黙の第1パラメーターを持たない。スタティックメソッドの定義では、一般にメソッド定義を「@staticmethod」デコレーターで修飾する。
これらをまとめると次のようになる。
class クラス名(基底クラス):
クラス変数名 = 初期値
def __init__(self, パラメーターリスト):
インスタンスの初期化処理
def インスタンスメソッド名(self, パラメーターリスト):
インスタンスメソッドのコード
@classmethod # クラスメソッドの定義
def クラスメソッド名(cls, パラメーターリスト):
クラスメソッドのコード
@staticmethod # スタティックメソッドの定義
def スタティックメソッド名(パラメーターリスト):
スタティックメソッドのコード
以下に例を示す。
class Foo:
clsvar = 0 # 今までに何個インスタンスが作成されたかを記録
def __init__(self, name=''):
Foo.clsvar += 1 # クラス変数の値をインクリメント
self.name = name
def instance_method(self):
print('in instance_method')
print('cls var:', self.clsvar) # self.clsvarとしてクラス変数を読み出し
print('instance var:', self.name)
@classmethod
def cls_method(cls):
print('in cls_method')
print('cls var:', cls.clsvar)
@staticmethod
def static_method():
print('in static_method')
print('cls var:', Foo.clsvar)
f = Foo('foo')
f.instance_method() # インスタンスメソッド呼び出しにはインスタンスを使う
f.cls_method() # クラスメソッド呼び出しにはインスタンスも使える
Foo.cls_method() # クラスメソッド呼び出しにはクラスも使える
Foo.static_method() # スタティックメソッド呼び出しにはクラスしか使えない
print()
b = Foo('bar')
b.instance_method()
print('Foo.clsvar:', b.clsvar)
b.clsvar += 1 # インスタンスbにインスタンス変数clsvarを定義してしまう
print('b.clsbar:', b.clsvar)
print('Foo.clsbar:', Foo.clsvar)
この例では、instance_methodメソッドではクラス変数の値を読み取るのに「self.clsvar」としている。一方、__init__メソッドではクラス変数の値を変更するのに「Foo.clsvar += 1」としている。読み取りに関しては「self.clsvar」で構わないが、「self.clsvar」に代入をすると、それを行ったインスタンス内にクラス変数と同じ名前のインスタンス変数を作ることになるので、__init__メソッドでは「Foo.clsvar += 1」としてクラス変数の値を変更している。
また、cls_methodメソッドでは「cls.clsvar」としてクラス変数にアクセスしている。これによりクラス変数にアクセスできる。なお、クラスメソッドは特定のインスタンスとは関連付けられておらず、暗黙の第1パラメーターclsにはクラス自体が渡される。
static_methodメソッドは暗黙の第1パラメーターに特定のインスタンスやクラスが渡されることはないので、その内部でクラス変数にアクセスするには「クラス名.クラス変数名」とするしかない。そのため、上の例では「Foo.clsvar」としてアクセスをしている。
このクラスのインスタンスを定義して、各メソッドを呼び出しているコードの下の方では、「b.clsvar += 1」としているが、これによりインスタンスbに「clsvar」という「インスタンス変数」が作成されてしまう。そのため、「b.clsvar」と「Foo.clsvar」ではその値が異なるようになる(興味のある方はコードを実行して確認してほしい)。
外部に公開したくない属性(インスタンス変数、メソッド)をクラスが持つ場合、それらの名前はアンダースコア「_」で始めるようにする。これはその属性が「プライベート」であることを意味する命名規約であり、それを使わないようにすることはあくまでも「紳士協定」である。
以下に例を示す。
class Foo:
def __init__(self, name):
self._name = name # _nameはプライベートな属性
def get_name(self): # _nameを読み取るためのメソッド
return self._name
def set_name(self, new_name): # _nameを書き換えるためのメソッド
self._name = new_name
f = Foo('insider.net')
print(f.get_name()) # name属性をメソッド経由で取得
f.set_name('deep insider')
print(f._name) # 1つのアンダースコアは「紳士協定」なので、直接アクセスも可能
最後の行で「f._name」としているように、アンダースコア「_」で始まる属性にも直接アクセス可能である。
あるいは、2つのアンダースコア「__」で始めることで、さらに強い効力を持たせることもできる。2つのアンダースコアで属性の名前を始めると、その名前で直接その属性にアクセスできなくなる(「_クラス名__属性名」のように名前がされる。これを「名前マングリング」あるいは単に「マングリング」と呼ぶ)。
以下に例を示す。
class Foo:
def __init__(self, name):
self.__name = name # __nameはプライベートな属性
def get_name(self): # __nameを読み取るためのメソッド
return self.__name
def set_name(self, new_name): # __nameを書き換えるためのメソッド
self.__name = new_name
f = Foo('insider.net')
print(f.get_name()) # name属性をメソッド経由で取得
f.set_name('deep insider')
print(f._Foo__name) # 無理やりにアクセスは可能
print(f._name) # AttributeError例外
この場合も、「f._Foo__name」とすれば、クラス内部で「__name」として定義されているインスタンス変数にアクセスは可能だが、以前のように「__name」で外部からはアクセスできなくなる。
プライベートな属性と、それらにアクセスするためのメソッド(インタフェース)を用意することはよくある。それらは上のような方法でも実現可能だが、Pythonでは「プロパティ」と呼ばれる機構を用いて、これを実現できる。
プロパティを設定するには組み込みのproperty関数を使用するか、@propertyデコレーター(セッタ、ゲッタなどのデコレーター指定)を使用する。
property関数の構文を以下に示す。
property(fget=None, fset=None, fdel=None, doc=None)
fgetにはプロパティの値を読み出すためのメソッドを、fsetにはプロパティの値を設定するためのメソッドを、fdelにはプロパティの値を削除するためのメソッドを、docにはプロパティのヘルプ文字列(docstring)を指定する。
以下に例を示す。
class Foo:
def __init__(self, name):
self.__name = name
def get_prop(self):
return self.__name
def set_prop(self, new_name):
if not isinstance(new_name, str):
raise TypeError('name attrib is str')
self.__name = new_name
name = property(get_prop, set_prop)
f = Foo('insider.net')
print(f.name) # 'insider.net'
f.name = 'deep insider'
print(f.name) # 'deep insider'
f.name = 0 # TypeError例外
この例では、__init__メソッドでプライベートなインスタンス変数__nameの値を初期化して、その後はget_propメソッドとset_propメソッドを介して、その値をやりとりするようなコードになっている。しかし、クラス定義の最後ではproperty関数を使用して、プロパティの値の読み取りはget_propメソッドで行い、書き込みはset_propメソッドで行うように指定し、その戻り値をname属性に代入している。これにより、「インスタンス変数nameを使ってその値を読み書きする」体裁で、これらのメソッドを使った値の読み書きを行えるようになる。
@propertyデコレーターを使用する例を以下に示す。
class Foo:
def __init__(self, name):
self.__name = name
@property
def name(self):
return self.__name
@name.setter
def name(self, new_name):
if not isinstance(new_name, str):
raise TypeError('name attrib is str')
self.__name = new_name
f = Foo('insider.net')
print(f.name) # 'insider.net'
f.name = 'deep insider'
print(f.name) # 'deep insider'
f.name = 0 # TypeError例外
@propertyデコレーターを使用する場合には、@propertyデコレーターで修飾したメソッドが値を読み取るために使われる(ゲッタ)。そのメソッドと同じ名前のメソッドを定義して、「@メソッド名.setter」デコレーター(この場合は「@name.setter」デコレーターとなる)で修飾することで、それがプロパティの値を設定するために使われる(セッタ)。「@メソッド名.deleter」デコレーターで修飾したメソッドがあれば、それはプロパティの値を削除するために使われる。
Copyright© Digital Advantage Corp. All Rights Reserved.