· 

PythonでOOP(Part 3)

前の編もお読みください:Part 1Part 2

 

1.Getting Started

  Pythonを使ってOOPすなわちObject-Oriented Programmingの解説をします。

今回は、class methodsとstatic methodsです。

 

 2.References

Youtubeとして、

Python OOP Tutorial 3: classmethods and staticmethods

また、Documentationとして、

https://docs.python.org/3/tutorial/classes.html

を挙げておきます。

 

3.前回までのCode

一部変更したりしていますが、今回使うcodeは次のものです。

class Employee:

 

    num_of_emps = 0         # class variable

    raise_amount = 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

        

        Employee.num_of_emps += 1 

 

    def fullname(self):

        return '{} {}'.format(self.first, self.last)

 

    def apply_raise(self):

        self.pay = int(self.pay * self.raise_amount)

 

emp_1 = Employee('Mark', 'Twain', 50000)

emp_2 = Employee('Test', 'User', 6000)

 

Class Employeeと、2つのinstancesであるemp_1とemp_2です。

 

4.Regular methods, Class methods, and Static methods

(1) regular methodsでは、自動的にinstanceを1つめのargumentとして扱います。

たとえば、init methodでは、

def __init__(self, first, last, pay):

となっていますが、これを呼び出すときは、

emp_1 = Employee('Mark', 'Twain', 50000)

であり、'Mark'は、2つめのargumentとして扱われます。

 

(2) class methodsでは、自動的にclassを1つめのargumentとして扱います。class methodsを作るには、decoratorとして

@classmethod

を冒頭につけます。そして、regular methodsでselfとするところを、cls (= class)としてmethodを組み立てます。

    @classmethod

    def set_raise_amt(cls, amount):

        pass

このcodeを、先ほどのcodeのclass Employeeの一番下に付け加えます。そして、runしてみます。

ここでは、出力の4~9行目の{...}の中にあるclassmethodに注目してください。

'set_raise_amt': <classmethod(...)>

となっており、classmethodとして扱われています。

 

(3) passを消して、

cls.raise_amt = amount

を書き入れます。さらに、set_raise_amtを実行し、raise_amtを出力するための命令として、

Employee.set_raise_amt(1.8)

 

print(Employee.raise_amt)

print(emp_1.raise_amt)

print(emp_2.raise_amt)

を付け加えて、runしてみます。

1つめの1.8は、class methodであるset_raise_amt()によって、Employee.raise_amt = 1.8に設定されたので、1.8が出力されました。2つめの1.8は、emp_1.raise_amtというattributeは存在しないので、classであるEmployeeが参照され、Employee.raise_amt=1.8なので、1.8が出力されました。3つめの1.8も同様です。(これについては、Part 2で詳しく説明してあります。)

 

このclass methodであるset_raise_amt()は、Employeeのattributeであるraise_amtを直接変更する命令である、

Employee.raise_amt = 1.8

と同じといえます。

 

 

5.constructor

(1) classmethodをalternative constructorとして使う方法を考えてみます。まずは、次のようなcodeがあるとします。

# new code

emp_str_1 = 'John-Doe-70000'

emp_str_2 = 'Steve-Smith-80000'

emp_str_3 = 'Jane-Dane-90000'

 

first, last, pay = emp_str_1.split('-')

new_emp_1 = Employee(first, last, pay)

 

print(new_emp_1.email)

print(new_emp_1.pay)

 

これをclass Employeeの下に置きます。そして、runしてみます。

先ほどのcodeは、氏名とpayが-でつながったstringがあって、それをfirst, last, payにsplit()して、さらに、Employeeでinstanceを作るというものでした。出力を見る通り、new_emp_1というinstanceがうまくできています。

 

(2) 次に、これをclass Employeeの中に、埋め込んでみます。まずは、regular methodとして作ってみます。

    def from_string(self, emp_str):

        first, last, pay = emp_str.split('-')

        Employee(first, last, pay)

ここまで書いてみて、なんかうまくいかなそうなことがわかってきます。regular methodは、それ以前に、init methodでfirst, last, payを使って、instanceを作らなければならないのですが、first, last, payを使うためには、instanceが作られていなければならないのです。これでは、できません。

 

では、class methodとして作るのはどうでしょうか。途中まで書いてみました。

    @classmethod

    def from_string(cls, emp_str):

        first, last, pay = emp_str.split('-')

        Employee(first, last, pay)

class methodの場合、instanceを作る以前に、class methodを呼び出すことができます。それゆえ、regular methodで生じた問題は、ここでは生じません。また、class自体を呼び出すことができるので、このcodeのEmployeeのところをclsと書き換えれば、うまくいきそうです。すなわち、

    @classmethod

    def from_string(cls, emp_str):

        first, last, pay = emp_str.split('-')

        cls(first, last, pay)

さらに、こうやって作ったcls(first, last, pay)はinstanceといえるので、これをreturnすれば、完成です。

    @classmethod

    def from_string(cls, emp_str):

        first, last, pay = emp_str.split('-')

        return cls(first, last, pay)

このconstructorの使い方は、次のようになります。すなわち、Employee.を付けて使います。

new_emp_1 = Employee.from_string(emp_str_1)

print(new_emp_1.email)

print(new_emp_1.pay)

これをrunしたのが、次の画像です。

上記(1)でやったのと、同じ結果が得られました。

 

6.Static methods

(1) regular methodsは、selfすなわち自分自身(instance)を、1つめのargumentとして扱います。class methodsは、clsすなわち自分自身(class)を、1つめのargumentとして扱います。static methodsは、自分自身を1つめのargumentとして扱うことはありません。この点で、static methodsは、通常のfunctionとよく似ているといえます。

 

(2) static methodsでは、decoratorとして

@staticmethod

を冒頭につけます。argumentとして、selfとかclsとかを使うことはなく、必要なargumentだけをかっこ内に書きます。

 

次のcodeを使います。このcodeをclass Employeeのmethodsの一番下に付け加えます。

    @staticmethod

    def is_workday(day):

        if day.weekday() == 5 or day.weekday() == 6:   # Satureday or Sunday

            return False

        return True

 

ここでは、datetime moduleのdate objectのweekday()というfunctionを使っています。date objectについては、こちらを参照してください。

date.weekday()

Return the day of the week as an integer, where Monday is 0 and Sunday is 6. For example, date(2002, 12, 4).weekday() == 2, a Wednesday. See also isoweekday().

 

ところで、static methodsを使うかどうかの判断ですが、classの中で定義するfunctionで、instanceもclass自体もaccessしない場合に、static methodsを使うことになります。

 

(3) 先ほどのstatic methodを実行するために、次のcodeをclass Employeeの外側、つまり、いちばん下に、indentしないで付け加えます。

import datetime

my_date = datetime.date(2025, 3, 25)   # constructor

 

print(Employee.is_workday(my_date))   # True

これをrunしてみます。

3/25は火曜日なので、weekdayにあたり、Trueが返されました。日付を変えて、いろいろ試してみてください。

 

次は、Part 4です。