[解決!Python]クラスを定義するには:解決!Python
インスタンス変数/インスタンスメソッド/クラスメソッド/スタティックメソッド/プロパティなどを含んだクラスの定義方法と注意点を紹介する。
データ属性(インスタンス変数)を持つクラス
class MyClass1:
def __init__(self, x, y):
self.x = x
self.y = y
mc = MyClass1(1, 2)
print(f'x: {mc.x}, y: {mc.y}') # x: 1, y: 2
説明は以下の「データ属性(インスタンス変数)を持つクラス」を参照のこと。
メソッド(インスタンスメソッド)を持つクラス
class MyClass2:
def __init__(self, x, y):
self.x = x
self.y = y
def show_attr(self):
print(f'x: {self.x}, y: {self.y}')
def set_x(self, value):
self.x = value
def set_y(self, value):
self.y = value
mc = MyClass2(1, 2)
mc.show_attr() # x: 1, y: 2
mc.set_x(10)
mc.set_y(20)
mc.show_attr() # x: 10, y: 20
説明は以下の「メソッド(インスタンスメソッド)を持つクラス」を参照のこと。
クラス変数を持つクラス
class MyClass3:
clsVar = 'class variable'
def __init__(self):
pass
def show_clsVar(self):
print(f'self.clsVar: {self.clsVar}')
print(f'MyClass3.clsVar: {MyClass3.clsVar}')
def change_clsVar(self, value):
self.clsVar = 'with self.clsVar: ' + value
MyClass3.clsVar = 'with MyClass3.clsVar: ' + value.upper()
mc = MyClass3()
mc.show_clsVar()
# 出力結果:
# self.clsVar: class variable
# MyClass3.clsVar: class variable
mc.change_clsVar('foo')
mc.show_clsVar()
# 出力結果:
# self.clsVar: with self.clsVar: foo
# MyClass3.clsVar: with MyClass3.clsVar: FOO
説明は以下の「クラス変数を持つクラス」を参照のこと。
クラスメソッドを持つクラス
class MyClass4:
clsVar = 'class variable'
def __init__(self):
pass
def show_clsVar1(self):
print(f'self.clsVar: {self.clsVar}')
@classmethod
def show_clsVar2(cls):
print(f'cls.clsVar: {cls.clsVar}')
@classmethod
def change_clsVar(cls, value):
print(f'brefore: {cls.clsVar}')
cls.clsVar = value
print(f'after: {cls.clsVar}')
mc = MyClass4()
mc.show_clsVar1() # self.clsVar: class variable
mc.show_clsVar2() # cls.clsVar: class variable
MyClass4.show_clsVar2() # cls.clsVar: class variable
mc.change_clsVar('changed')
MyClass4.show_clsVar2() # cls.clsVar: changed
説明は以下の「クラスメソッドを持つクラス」を参照のこと。
スタティックメソッド(静的メソッド)を持つクラス
class MyClass5:
PI = 3.14159
def __init__(self):
pass
@staticmethod
def double(x):
return x * 2
@staticmethod
def square(x):
return x ** 2
@staticmethod
def area_of_circle(r):
return MyClass5.PI * r ** 2
y = MyClass5.double(2)
print(y) # 4
y = MyClass5.square(4)
print(y) # 16
y = MyClass5.area_of_circle(1)
print(y) # 3.14159
mc = MyClass5()
y = mc.square(3)
print(y) # 9
説明は以下の「スタティックメソッド(静的メソッド)を持つクラス」を参照のこと。
プロパティを持つクラス(1)
class MyClass6:
def __init__(self, x):
self._x = x
def get_x(self):
return self._x
def set_x(self, value):
self._x = value
def del_x(self):
del self._x
x = property(get_x, set_x, del_x, 'property x')
mc = MyClass6(100)
print(mc.x) # 100
mc.x = 200
print(mc.x) # 200
del mc.x
print(mc.x) # AttributeError
mc.x = 10
print(mc.x) # 10
説明は以下の「プロパティを持つクラス(1)」を参照のこと。
プロパティを持つクラス(2)
class MyClass7:
def __init__(self, x):
self._x = x
@property
def x(self):
return self._x
@x.setter
def x(self, value):
self._x = value
@x.deleter
def x(self):
del self._x
mc = MyClass7(100)
print(mc.x) # 100
mc.x = 200
print(mc.x) # 200
del mc.x
print(mc.x) # AttributeError
mc.x = 10
print(mc.x) # 10
説明は以下の「プロパティを持つクラス(2)」を参照のこと。
データ属性(インスタンス変数)を持つクラス
Pythonのクラスはclass文で定義する。クラスにはさまざまな属性を持たせられるが、その中でも必ずといっていいほど使うことになるのがデータ属性(インスタンス変数)だ。
データ属性を含むクラスを定義する例を以下に示す。
class MyClass1:
def __init__(self, x, y):
self.x = x
self.y = y
これにより関数のようにして呼び出し可能なMyClass1オブジェクト(クラスオブジェクト)が作成される。そして、「MyClass1(1, 2)」のようにして、これを呼び出すことでMyClass1クラスのインスタンス(オブジェクト)が作成される。
「MyClass(1, 2)」のようにして呼び出した際に指定した引数は、__init__メソッドと呼ばれる、インスタンスの初期化を行うためのメソッドへと渡される。このメソッドの先頭パラメーター「self」はインスタンス自身であり、インスタンス生成時にプログラマーが明示的に指定する必要はない(メソッドが呼び出される際にPython処理系により自動的に設定される)。
__init__メソッドの内部では、初期化の対象となるインスタンス自身「self」と、そのパラメーターを使って、何らかの初期化処理を行う。上の例では、自身の属性「self.x」「self.y」に2つのパラメーターの値を渡すことで初期化を行っている(これは典型的な例)。
実際のインスタンス生成の例を以下に示す。
mc = MyClass1(1, 2)
print(f'x: {mc.x}, y: {mc.y}') # x: 1, y: 2
メソッド(インスタンスメソッド)を持つクラス
データ属性と共にクラスでよく使われるのが、インスタンスが持つ各種属性を使って何らかの処理を行うメソッド(インスタンスメソッド)だ。__init__メソッドがそうだったように、通常のメソッドでは自身を参照するselfが第1パラメーターとなり、その後にメソッド呼び出し時に使用する各種の値を受け取るパラメーターが並ぶ。__init__メソッドと同様、呼び出し側がselfを明示的に指定する必要はなく、「インスタンス.メソッド(self以外のパラメーターに渡す値)」として呼び出せば、呼び出しに使用したメソッドが自動的にselfに代入される。
以下にメソッド(インスタンスメソッド)を持つクラスの例を示す。
class MyClass2:
def __init__(self, x, y):
self.x = x
self.y = y
def show_attr(self):
print(f'x: {self.x}, y: {self.y}')
def set_x(self, value):
self.x = value
def set_y(self, value):
self.y = value
この例では、show_attrメソッドにはself以外にパラメーターがない。そのため、このメソッドは引数なしで呼び出しを行う。set_xメソッドとset_yメソッドは、__init__メソッドで初期化したx属性とy属性の値を書き換えるもので、呼び出し時には引数を1つ指定する。
実際の使用例を以下に示す。
mc = MyClass2(1, 2)
mc.show_attr() # x: 1, y: 2
mc.set_x(10)
mc.set_y(20)
mc.show_attr() # x: 10, y: 20
クラス変数を持つクラス
あるクラスのインスタンス全てで共有したい情報がある場合、それらはクラス変数に保存しておくのが一般的だ。class文の直下で(メソッド定義の外側で)変数を定義すると、それがクラス変数となる。
クラス変数には、インスタンスメソッド/クラスメソッド(後述)/スタティックメソッド(後述)からアクセスが可能だ。そのアクセスの仕方には大きく2種類がある。インスタンスメソッドでは「self.クラス変数名」あるいは「クラス名.クラス変数名」としてアクセスが可能だ。他の2つのメソッドからは「クラス名.クラス変数名」としてアクセスする。
値を参照するだけなら、2つの方法のどちらを使っても問題ないが、クラス変数に値を代入するのであれば、「クラス名.クラス変数名 = 値」とする必要がある点には注意すること。「self.クラス変数名 = 値」はクラス変数名と同じ名前の属性をデータ属性として自身に追加するだけだ。
以下に例を示す。
class MyClass3:
clsVar = 'class variable' # クラス変数
def __init__(self):
pass
def show_clsVar(self):
print(f'self.clsVar: {self.clsVar}') # selfを使ってアクセス
print(f'MyClass3.clsVar: {MyClass3.clsVar}') # クラス名を使ってアクセス
def change_clsVar(self, value):
self.clsVar = 'with self.clsVar: ' + value # self.clsVar属性を追加
MyClass3.clsVar = 'with MyClass3.clsVar: ' + value.upper()
この例のshow_clsVarメソッドでは、上で述べた「self.クラス変数名」「クラス名.クラス変数名」の両方の手段でクラス変数clsVarの値を読み出している。一方、change_clsVarメソッドでは同じく2つの方法でclsVarに代入をしている。
このクラスを実際に使う例を示す。
mc = MyClass3()
mc.show_clsVar()
# 出力結果:
# self.clsVar: class variable
# MyClass3.clsVar: class variable
読み出しではどちらも同じクラス変数にアクセスしているので、同じ値が表示されている。しかし、change_clsVarメソッドを呼び出すと次のようになる。
mc.change_clsVar('foo')
mc.show_clsVar()
# 出力結果:
# self.clsVar: with self.clsVar: foo
# MyClass3.clsVar: with MyClass3.clsVar: FOO
change_clsVarメソッドにある「self.clsVar = ……」と「MyClass3.clsVar = ……」はどちらもクラス変数clsVarの値を変更しようとしているが、前者ではself(このメソッドの呼び出しで使用したインスタンス)にデータ属性として「clsVar」を追加してしまう。そのため、その後のshow_clsVarメソッドを呼び出すと、別々の値が表示されるようになる。
クラスメソッドを持つクラス
クラスメソッドは、インスタンスメソッドとは異なり、クラスが持つ属性(クラス変数)などを使って何らかの処理を実行するために使用する。クラスメソッドを定義するには、@classmethodデコレーターでクラスメソッドにしたいメソッドを修飾すればよい。
注意する点は、クラスメソッドの第1パラメーターにはインスタンスではなく、クラスのオブジェクトが代入される点だ。このことを明示するためにクラスメソッドの第1パラメーターの名前は「cls」とすることが推奨されている。そのため、クラスメソッドの中では特定のインスタンスが持つ値(データ属性)を参照/変更したり、インスタンスメソッドを呼び出したりすることはできない。
以下にクラスメソッドを持つクラスの定義例を示す。
class MyClass4:
clsVar = 'class variable'
def __init__(self):
pass
def show_clsVar1(self):
print(f'self.clsVar: {self.clsVar}')
@classmethod
def show_clsVar2(cls):
print(f'cls.clsVar: {cls.clsVar}')
@classmethod
def change_clsVar(cls, value):
print(f'brefore: {cls.clsVar}')
cls.clsVar = value
print(f'after: {cls.clsVar}')
show_clsVar1メソッドは@classmethodデコレーターで修飾されていないので、通常のインスタンスメソッドであり、内部では「self.clsVar」のようにしてクラス変数にアクセスしている。
show_clsVar2メソッドとchange_clsVarメソッドは@classmethodデコレーターで修飾されているので、これらはクラスメソッドとなる。どちらも第1パラメーターがclsとなっていて、そこにクラスオブジェクトを受け取り、それを利用して「cls.clsVar」のようにクラス変数にアクセスしている点に注目されたい。
使用例を以下に示す。
mc = MyClass4()
mc.show_clsVar1() # self.clsVar: class variable
mc.show_clsVar2() # cls.clsVar: class variable
MyClass4.show_clsVar2() # cls.clsVar: class variable
mc.change_clsVar('changed')
MyClass4.show_clsVar2() # cls.clsVar: changed
クラスメソッドは「クラス名.クラスメソッド名(引数)」のようにして呼び出すのが一般的だ。だが、「mc.show_clsVar2(……)」のようにインスタンスを経由しても呼び出せる。
スタティックメソッド(静的メソッド)を持つクラス
スタティックメソッド(静的メソッド)は、クラスのインスタンスともクラスオブジェクトとも関連のないメソッドだ。つまり、クラスを名前空間のように使用して、関連のある操作(メソッド)をひとまとめにするために使われることがある。第1パラメーターにはインスタンスを参照する「self」も、クラスを参照する「cls」もないことに注意。
スタティックメソッドを定義するには、そのメソッドを@staticmethodデコレーターで修飾すればよい。スタティックメソッドは特定のインスタンスとの結び付きがないので、もちろんインスタンスのデータ属性にアクセスしたり、インスタンスメソッドを呼び出したりはできない。が、クラス変数については「クラス名.クラス変数名」としてアクセスできる。
以下にスタティックメソッドを持つクラスの定義例を示す。
class MyClass5:
PI = 3.14159
def __init__(self):
pass
@staticmethod
def double(x):
return x * 2
@staticmethod
def square(x):
return x ** 2
@staticmethod
def area_of_circle(r):
return MyClass5.PI * r ** 2
このコードではクラス変数PIが定義されていることと、double、square、area_of_circleの3つのメソッドが全て@staticmethodデコレーターで修飾されていること、それからarea_of_circleスタティックメソッドの内部では「MyClass5.PI」としてクラス変数PIにアクセスしている点に注目されたい。
このクラスを使用する例を以下に示す。
y = MyClass5.double(2)
print(y) # 4
y = MyClass5.square(4)
print(y) # 16
y = MyClass5.area_of_circle(1)
print(y) # 3.14159
mc = MyClass5()
y = mc.square(3)
スタティックメソッドを呼び出すには「クラス名.スタティックメソッド名(引数)」とする。だが、最後の例のようにインスタンスを生成し、それを介してスタティックメソッドを呼び出すことも可能だ。
プロパティを持つクラス(1)
プロパティは、インスタンスのデータ属性のように見えるが、その値の読み出し/書き込み/削除において、直接、その値を操作するのではなく、間にロジックを組み込むことで入力値のチェックをするといったことを可能にする機能だ。
Pythonでは、2つの方法でプロパティを定義できる。最初の方法はプロパティの操作に使用するメソッドを定義して、property関数でそれらをプロパティとしてひとまとめにして名前を付けるものだ。
今述べたように、プロパティの操作としては、値の読み出し(ゲッター)、値の書き込み(セッター)、値の削除(デリーター)がある。これらを全て定義してもよいし、必要なものだけを定義してもよい。そして、それらをproperty関数に渡す(このときには、ドキュメントコメントを第4引数に指定してもよい)。
以下に例を示す。
class MyClass6:
def __init__(self, x):
self._x = x
def get_x(self):
return self._x
def set_x(self, value):
self._x = value
def del_x(self):
del self._x
x = property(get_x, set_x, del_x, 'property x')
この例では、_x属性の値を読み出すget_xメソッド(ゲッター)、_x属性の値を設定するset_xメソッド(セッター)、_x属性を削除するdel_xメソッド(デリーター)を定義して、それらを「x = property(get_x, set_x, del_x, 'property x')」としてproperty関数に渡している。戻り値を代入している「x」がこのプロパティの名前となる。
以下に使用例を示す。
mc = MyClass6(100)
print(mc.x) # 100
mc.x = 200
print(mc.x) # 200
del mc.x
print(mc.x) # AttributeError
mc.x = 10
print(mc.x) # 10
インスタンスのx属性と同じようにして、_x属性にアクセスできている点に注目しよう。
プロパティを持つクラス(2)
プロパティを定義するもう1つの方法は@propertyデコレーターを使用するものだ。具体的には、ゲッターとなるメソッドを@propertyデコレーターで修飾する。そのメソッドの名前がプロパティの名前となる。そして、セッターやデリーターが必要であれば、同じ名前のメソッドを定義して、セッターは@プロパティ名.setterデコレーターで、デリーターは@プロパティ名.deleterデコレーターで修飾する。
以下に例を示す。これは上で見たproperty関数を使ったプロパティの定義と同じことをする。ゲッター、セッター、デリーターとなるメソッドの名前が全て「x」である点に注意。そして、この「x」がプロパティの名前となる。
class MyClass7:
def __init__(self, x):
self._x = x
@property
def x(self):
return self._x
@x.setter
def x(self, value):
self._x = value
@x.deleter
def x(self):
del self._x
使用例を以下に示す。
mc = MyClass7(100)
print(mc.x) # 100
mc.x = 200
print(mc.x) # 200
del mc.x
print(mc.x) # AttributeError
mc.x = 10
print(mc.x) # 10
Copyright© Digital Advantage Corp. All Rights Reserved.