前の編もお読みください:Part 1、Part 2、Part 3、Part4
1.Getting Started
Pythonを使ってOOPすなわちObject-Oriented Programmingの解説をします。
今回は、Special (Magic/Dunder) Methodsです。
2.References
Youtubeとして、
Python OOP Tutorial 5: Special (Magic/Dunder) Methods
また、Documentationとして、
https://docs.python.org/3/tutorial/classes.html
を挙げておきます。
3.前回までのCode
一部変更したりしていますが、今回使うcodeは次のものです。
class Employee:
raise_amt = 1.04 # class variable
def __init__(self, first, last, pay):
self.first = first
self.last = last
self.email = first + '.' + last + '@company.com'
self.pay = pay
def fullname(self):
return '{} {}'.format(self.first, self.last)
def apply_raise(self):
self.pay = int(self.pay * self.raise_amt)
emp_1 = Employee('Mark', 'Twain', 50000)
emp_2 = Employee('Test', 'User', 6000)
print(emp_1.email)
print(emp_1.fullname())
4.Special methods
(1) special methodsは、init methodのように
__methodname__
すなわち、method nameの前後をdouble underscoreで、挟み込んだ形をとります。double underscoreのことを、dunderと呼びます。
(2) ここでは、2つのspecial methodsを紹介します。repr(ar-ee-pe-arと読みます)methodとstr methodです。ひとまず、中身を pass だけにして、class Employee: の、一番下に付け加えます。
def __repr__(self):
pass
def __str__(self):
pass
(3) まずは、repr methodの中身を書き換えます。
def __repr__(self):
return "Employee({}, {}, {})".format(self.first, self.last, self.pay)
#def __str__(self):
# pass
reprでは、Employee()のかっこの中に、first, last, payの中身を入れる形になっています。また、str methodは、#を付けて動かないようにしておきます。さらに、codeの一番下に、
print(emp_1)
を追加します。
(4) まず、repr methodがない場合にどうなるかを調べるために、先ほどのrepr methodをcomment化します。
#def __repr__(self):
# return "Employee({}, {}, {})".format(self.first, self.last, self.pay)
これをrunします。
出力結果の最初の2行は、もともとあったprint()文によるものです。3行目は、
print(emp_1)
の通常の出力結果です。Employeeというobjectであることと、addressらしきものが表示されます。emp_1がどういうものであるかを調べたいときに、上の2行のように、emp_1.emailとか、emp_1.fullname()により、具体的なattributeごとに調べなくてはならないのは、少し面倒です。
(5) そこで、repr methodの#を取り除き、動くようにして、もう1度、runしてみます。
3行目は、やはり、
print(emp_1)
の出力結果です。しかし、今度は、repr methodが自動的に動いて、address等ではなくて、class名のEmployeeとそのattributesのMark, Twain, 5000が表示されました。
このように、repr methodは、instance名をprintしようとしたときに、本来は、object名とaddressが表示されるところで、object名とattributesの内容が表示されるようにするものといえます。
5.dunder str method
(1) 次は、str methodです。次のcodeに書き換えます。
def __repr__(self):
return "Employee({}, {}, {})".format(self.first, self.last, self.pay)
def __str__(self):
return '{} - {}'.format(self.fullname(), self.email)
このstr methodでは、full nameとemailが dashでつながれて表示されます。repr methodも動く状態のままで、codeをrunしてみます。
出力結果の3行目が、
print(emp_1)
によるものです。repr methodがあるにもかかわらず、str methodのみがcall されて、full nameとemailが表示されました。このように、str methodがあるときは、repr methodは完全に無視されます。
(2) そうすると、repr methodが意味がないものとなってしまいます。そこで、それぞれのmethodを個別に使いたいときは、print(emp_1)でなくて、
print(repr(emp_1))
の形で書きます。これをrunすると、
となり、repr methodで表示されました。したがって、str methodの場合は、
print(emp_1)
print(str(emp_1))
のどちらでも、表示できます。runしてみます。
同じものが2回表示されました。
(3) さらに、print()文の箇所を、次の4行のcodeに書き換えます。
print(repr(emp_1))
print(str(emp_1))
print(emp_1.__repr__())
print(emp_1.__str__())
これをrunしてみます。
上下で同じ結果が出ました。
つまり、repre(emp_1)とemp_1.__repr__()は同じであり、str(emp_1)とemp_1.__str__()は同じということになります。
6.dunder add method
(1) dunder methodsで頻繁に使うのは、init method, repr method, str methodの3つですが、他の例も挙げてみます。
print(1+2)
print(int.__add__(1,2))
2行目は、+のbackgroundで使われているcodeです。the integer objectのdunder add methodを使って、1+2を行っているわけです。これをrunします。
同じ結果が2つ出ました。
(2) stringの+(concatenation)も同様です。
print('a'+'b')
print(str.__add__('a', 'b'))
2行目では、the string object のdunder add methodが使われています。
やはり、同じ出力結果が出ました。
(3) これらと同じようなdunder add methodをEmployeeの中に作ってみます。
def __add__(self, other):
return self.pay + other.pay
このcodeをclass Employee:の一番下に付け加え、さらに、
emp_1 = Employee('Mark', 'Twain', 50000)
emp_2 = Employee('Test', 'User', 6000)
の下に、
print(emp_1 + emp_2)
print(emp_1.__add__(emp_2))
を付け加えます。これをrunしてみます。
1行目は、+記号によるもの、2行目は、dunder add methodをそのまま使ったものです。どちらも、正しい答えが出力されました。
(4) このようなdunder methodeは、Python Documentation の3.3.8. Emulating numeric typesにて、見つけることができます。
(5) もう1つdunder methodを作ってみます。
def __len__(self):
return len(self.fullname())
これは、full nameのlengthをreturnするmethodです。その使用codeとして、
print(emp_1.fullname())
print(len(emp_1))
print(emp_1.__len__())
をcodeの一番下に付け加えます。そして、runします。
