クラス

ここではPythonのクラスについて学習します。いわゆるオブジェクト指向プログラミングをサポートするものですが、ここではオブジェクト指向とは何かとか、関数型うんぬんの話はしません。純粋にこんな風に書いて、こんな風に動かすんだなという部分について記載します。

ここで学習すること

  • クラス

サンプルコード

person.py

#!/usr/bin/env python3.8

class Person():
	def __init__(self, name = "ken", age = 10):
		self.name = name
		self.age = age

	def __str__(self):
		return self.name + " " + str(self.age)
	
	def to_dict(self):
		return {"name": self.name, "age": self.age}

class Staff(Person):
	is_staff = True

	def to_dict(self):
		return {"name": self.name, "age": self.age, "is_staff": Staff.is_staff}

class Member(Person):
	is_staff = False

	def to_dict(self):
		return {"name": self.name, "age": self.age, "is_staff": Member.is_staff}

sample.py

#!/usr/bin/env python3.8

from person import Member

if __name__ == "__main__":
	member = Member("john", 22)
	print(member)
	print(member.to_dict())

説明

クラスは次のように書き始めます

class Person():

:で終わっているのでお分かりの通り、文と式とインデントで説明した通り次の行からインデントすることでブロックを構成し、クラスの定義が為される構図です。

他のクラスを継承する場合には、括弧内にそのクラスを記載します。

class Staff(Person):

Personクラスを継承してStaffクラスを定義しています。

def __init__(self, name = "ken", age = 10):
   self.name = name
   self.age = age

__init__()はクラスのコンストラクタです。クラスのインスタンスが生成される時、その名の通りここに記載の処理でインスタンスが初期化されます。この例ではクラス内のインスタンス変数であるnameとageをセットしています。名前も年齢も指定されなかった場合にはケンくん10歳になるようです。

クラスメソッドは第1引数に必ずselfを置きます。ここではその理由というか利点というか言語仕様の話はしません。ですが必ずself を置きましょう! 第1引数に「selfが最も端的に言い表しているもの」が入ってくるのでselfとしましょう。selfの代わりにaとかbとかにしても動作するなんて気づいちゃったとしてもやらないでください。でもaでもbでも動くと知ったら別の「おまじない感」がして、ほんの少しだけ気が楽になりましたでしょうか?

def __str__(self):
    return self.name + " " + str(self.age)

続いて__str__()です。このメソッドは、文字列に変換することを要求されたときに使われます。print文の引数に指定されたときなどに使われます。もちろん定義しなかったからといってなにか咎められることはありません。が、きっと残念な結果を見ることになります。

__から始まる特殊な?メソッドに__init__()や__str__()以外に何があるかについては、やはり公式ドキュメントを参照すべきです。代表者として__init__()と__str__()を紹介しました。

続いては利用する側です。

from person import Member

person.pyファイルの中で定義されたMemberクラスを利用するのでこのように記載します。この部分については、import文で解説しています。

member = Member("john", 22)

Javaなど他の一部の言語ではnewなどの演算子が用意されていますが、Pythonにはありません。上記の通り、そのままむき出しのままメソッドを使うかのごとく書くことでインスタンスが新たに生成されます。

print(member)

インスタンスそのものをプリントしようとしています。先ほど述べた通り__str__()メソッドを経由して無事に表示されるはずです。

print(member.to_dict())

続いてto_dict()メソッドを呼び出しています。Pythonのメソッドやクラス変数はどれも外部からアクセス可能です。Javaを例に取るとどれもpublic修飾子付きということです。

それでは実行してみましょう。

実行結果

$ python sample.py 
john 22
{'name': 'john', 'age': 22, 'is_staff': False}
$ 

期待通りでしょうか。

続いて__str__メソッドの部分をコメントアウトして実行してみてください。

実行結果

(LearningPython) MacBookPro:100.クラス $ python sample.py
<person.Member object at 0x10f7373a0>
{'name': 'john', 'age': 22, 'is_staff': True}
(LearningPython) MacBookPro:100.クラス $

はい。これが「お咎めは無い」という状態です。ドキュメントを読めば分かりますが、__str__()が実装されていないので、代わりに__repr__()が呼び出されたという状態です。

このあたり気になる方は探求し、とりま動くものを求めている方は先に進んでしまいましょう。

まとめ

クラスにはメソッドとクラス変数が定義できます。クラス変数が全てのインスタンスで共有する変数なのに対して、インスタンス変数はインスタンス毎に持つ変数です。なのでself.xxx という形で使用しました。メソッドも変数もすべてpublicな状態なので、外部からアクセスし放題です。

最後に

クラスにto_dict()というメソッドを定義していますが、これは実際にはvars() 関数で事足りたり、足りなかったりで、そのあたりはご自身の設計に照らして実装頂ければと思います。