オブジェクト(class)の作成-その4
記事の内容
この記事では、自分が作成したクラスオブジェクトを使った2項演算を定義する方法について説明します。2項演算とは、算術演算子("+", "-", "*". "/"など)や、比較演算子(">", ">=", "<", "<=", "=="など)を使った式のことをいいます。
***わからない用語があるときは索引ページへ***
この記事のサンプルプログラムは、Python3系をベースとしています。Python2系は、クラス宣言の記述ルールが異なるのでご注意ください。
1.オブジェクトの加算(+)を定義するサンプルプログラム
class_ex4.pyは、特殊関数__add__関数を使って、KagensanクラスのインスタンスとKagensanクラスのインスタンスの加算を定義するサンプルプログラムです。記事の後ろで、サンプルプログラムの説明をします。
##class_ex4.py
##class宣言
class Kagensan:
##インスタンスコンストラクタの作成
def __init__(self, x, y):
self.x = x
self.y = y
##インスタンスメソッドの作成
def tashizan(self):
wa = self.x + self.y
return wa
def hikizan(self):
sa = self.x - self.y
return sa
##__add__関数はオブジェクトの加算を定義する特殊メソッド。
##第2引数に、加算するオブジェクトを指定する
def __add__(self, other):
##引数のオブジェクト(other)の型チェック
##加算するオブジェクトの型が指定した型でなければ
##NotImpremented例外を返して処理を強制終了させる
if not isinstance(other, Kagensan):
return NotImplemented
##加算の処理をプログラムして結果を返す
xx = self.x + other.x
yy = self .y + other.y
add_instance = Kagensan(xx, yy)
##ここでは、前の行で生成されたKagensanクラスのインスタンスを返している
return add_instance
##加算されるオブジェクトと加算するオブジェクトの順番が逆になっても
##同じ演算をしたい場合は、この行を追加する。
##この例では、加算されるオブジェクトも加算するオブジェクトも同じ型と
##なっているので、この行がなくても同じうごきをする。
__radd__ = __add__
##プログラム実行開始位置
ins1 = Kagensan(1, 10)
ins2 = Kagensan(10, 100)
##2つのオブジェクトの足し算
ins3 = ins1 + ins2
##2項演算で定義された演算子を使った演算式であれば、
##普通の数式と同じように計算できる
ins4 = ins1 + ins2 + ins3
##各インスタンスのインスタンス変数を出力してみる
##各インスタンスをのインスタンス変数とメソッドの実行結果を出力してみる
print("ins1.x =",ins1.x, "ins1.y =", ins1.y, "tashizan:", ins1.tashizan(), "hikizan:", ins1.hikizan())
print("ins2.x =",ins2.x, "ins2.y =", ins2.y, "tashizan:", ins2.tashizan(), "hikizan:", ins2.hikizan())
print("ins3.x =",ins3.x, "ins3.y =", ins3.y, "tashizan:", ins3.tashizan(), "hikizan:", ins3.hikizan())
print("ins4.x =",ins4.x, "ins4.y =", ins4.y, "tashizan:", ins4.tashizan(), "hikizan:", ins4.hikizan())
出力結果
ins1.x = 10 ins1.y = 1 tashizan: 11 hikizan: 9
ins2.x = 100 ins2.y = 10 tashizan: 110 hikizan: 90
ins3.x = 110 ins3.y = 11 tashizan: 121 hikizan: 99
ins4.x = 220 ins4.y = 22 tashizan: 242 hikizan: 198
2.__add__メソッドと__radd__メソッド
__add__関数を、
def __add__(<オブジェクト1>, <オブジェクト2>):
の形式で宣言すると、__add__の中身のプログラムブロックが、プログラムの中で
<オブジェクト1> + <オブジェクト2>
と記述されたときの処理の定義であることを意味しています。
視点をかえると、プログラムの中で
<オブジェクト1> + <オブジェクト2>
と記述され、実行されると、
__add__(<オブジェクト1>, <オブジェクト2>)
が呼び出され、実行されます。
サンプルプログラムの該当部分を見てみましょう。
def __add__(self, other):
第一引数のオブジェクトはself、つまり自分自身のインスタンスを表しており、第2引数で、自分自身のインスタンスに加算するオブジェクトを渡されるようになっています。
__add__メソッドのプログラムブロックの中身をみていきましょう。自分自身に加算することのできるオブジェクトの型(クラス)は何でも良いというわけにはいきません。そこで、組み込み関数であるisinstance関数を使って、まず最初にotherの型をチェックしています。otherの型が違っていれば特殊値を返してそこで処理が終了するようになっています。
if not isinstance(other, Kagensan):
return NotImplemented
isinstance関数は、第1引数(other)のオブジェクトの型が第2引数(Kagensanクラス)の型である場合にTrue、そうでない場合はFalseを返します。
したがって、if文の条件式では、加算する(演算子+の後ろに来る)オブジェクトがKagensanクラスでなければNotImprementedを返しています。これは、加算するオブジェクトがKagensanクラスでなければ、NotImprementedを返して処理が終了することを意味しています。
NotImprementedは、Pythonの特殊変数であり、二項演算のメソッドが、他の型に対して演算が実装されていないことをインタープリタに示す特殊値がセットされています。
次の行からが、オブジェクトの加算処理の定義になります。isinstance関数でチェック済なので、otherはKagensanクラスのインスタンスとなります。
最初の2行で、自分自身のインスタンス変数に、otherのインスタンス変数を加算しています(xxとyy)。つぎに、加算された数xxとyyを引数とするKagensanクラスのインスタンスを新しく生成して返しています。
xx = self.x + other.x
yy = self .y + other.y
add_instance = Kagensan(xx, yy)
##ここでは、前の行で生成されたKagensanクラスのインスタンスを返している
return add_instance
プログラム実行開始位置の記述をみてみましょう。
ins1 = Kagensan(1, 10)
ins2 = Kagensan(10, 100)
ins3 = ins1 + ins2
Kagensanクラスの2つのインスタンスを生成後、3行目で加算しています。この3行目の
ins3 = ins1 + ins2
では、
__add__(ins1, ins2)
が呼び出されますので、その結果、ins3は、
ins3.x = ins1.x + ins2.x -> 11
ins3.y = ins1.y + ins2.y -> 110
のインスタンス変数をもつKagensanクラスのインスタンスとなります。
つづいて、Kagensanクラスの次の行を見てみましょう。
これは、__add__メソッドの第1引数と第2引数の順番が逆であっても、同じ処理をするということを意味しています。処理の仕組を説明すると長くなってしまいますので、ここでは省略します。
__radd__ = __add__
なお、ここで定義した__add__メソッドは、第1引数も第2引数もKagensanクラスのオブジェクトなので、足し算の順番を入れ替えても第1引数と第2引数はKagensanクラスのオブジェクトとなります。したがって、__radd__ = __add__は省略しても同じ動きをします。
以前の記事で使い方を説明したdatetimeクラスでは、datetimeクラスのオブジェクトにtimedeltaクラスのオブジェクトを加算できることを説明しました。このように、異なる型で2項演算の加算を定義するときは、__radd__ = __add__を書いておかないと、加算の順番が逆になったときに例外扱いされてしまいます。
以下、class_ex4.pyの実行結果です。
ins1.x = 10 ins1.y = 1 tashizan: 11 hikizan: 9
ins2.x = 100 ins2.y = 10 tashizan: 110 hikizan: 90
ins3.x = 110 ins3.y = 11 tashizan: 121 hikizan: 99
ins4.x = 220 ins4.y = 22 tashizan: 242 hikizan: 198
3.比較演算子を使った2項条件式の定義
class_ex4_2.pyでは、__lt__関数を使った条件式の定義の例です。基本的な考え方、作成方法は__add__と変わりませんので、参考にしてみてください。
__lt__(a, b)は、演算子"<"を使って、a < bと記述されたときの条件式の定義をしています。class_ex4_2.pyでは、__lt__関数の第1引数のインスタンス変数の合計値が第2引数のインスタンス変数の合計値よりも小さければTrueとなるように定義しています。
##class_ex4_2.py
##class宣言
class Kagensan:
##インスタンスコンストラクタの作成
def __init__(self, x, y):
self.x = x
self.y = y
##インスタンスメソッドの作成
def tashizan(self):
wa = self.x + self.y
return wa
def hikizan(self):
sa = self.x - self.y
return sa
##比較演算子">"の2項演算の定義
##第1引数(self) < 第2引数(other)を判定
def __lt__(self, other):
##引数のオブジェクトの型チェック
##加算するオブジェクトの型が指定した型でなければ
##NotImpremented例外を返して処理を強制終了させる
if not isinstance(other, Kagensan):
return NotImplemented
wa1 = self.tashizan()
wa2 = other.tashizan()
##第1引数selfのインスタンス変数の和が、
##第2引数otherのインスタンスの和よりも小さければ
##self < otherがTrue、それ以外はFalseを返す
if wa1 < wa2:
hantei = True
else:
hantei = False
return hantei
##プログラム実行開始位置
ins1 = Kagensan(10, 1)
ins2 = Kagensan(100, 10)
##呼び出して使ってみる
print("ins1 < ins2 ?", ins1 < ins2)
print("ins2 < ins1 ?", ins2 < ins1)
print("\n")
##小さなインスタンスの方で計算結果を返す
if ins1 < ins2:
print("ins1.tashizan() =", ins1.tashizan())
else:
print("ins2.tashizan() =", ins2.tashizan())
出力結果
ins1 < ins2 ? True
ins2 < ins1 ? False
ins1.tashizan() = 11
4.2項演算の演算子を定義する特殊関数一覧
Pythonでは、__add__や__lt__のように、Pythonで使える2項演算子に対して、オブジェクトの2項演算を定義する特殊関数を用意しています。定義の仕方は__add__や__lt__と同じです。
四則演算を全て定義すると、式の中の()なども含めて、
a + b *c +(d - e) / f
のような長い式も、算術四則演算の規則どおりの順番で演算をしてくれます。
以下は、オブジェクトの2項演を定義する主な特殊関数です。
算術演算を定義する特殊関数
__add__(a, b):a+b
__sub__(a, b):a - b
__mul__(a, b):a * b
__truediv__(a, b):a / b
比較演算を定義する特殊関数
__eq__(a, b):a == b
__ne__(a, b):a != b
__lt__(a, b):a < b
__le__(a, b):a <= b
__gt__(a, b):a > b
__ge__(a, b):a >= b
他の2項演算を定義する特殊関数は、Python公式ページを参照ください(3.8.3の公式ページ)
この記事が気に入ったらサポートをしてみませんか?