Python3の復習(特殊メソッドとその他諸々)
こんにちは。
数学復習中のかにたまごです。
今は微積の復習してます。高校では習ってないかつ数弱なので呑み込みが悪い(;^ω^)
数学に関してはコツコツ基礎を固めていきます。
さてさて、今回は
- 特殊メソッド
- その他諸々(コンポジション・名前付きタプル・メソッドのタイプ)
について、復習していきたいと思います。
今までのPython3 復習履歴は以下の通り。
- 数値・文字列
- リスト・タプル
- 辞書・集合
- if文・while文・for文
- 内包表記
- 関数
- ジェネレータ
- デコレータ
- 名前空間とスコープ
- アンダーバー・アンダースコアの意味
- エラー処理:try・except
- モジュール・パッケージ
- 標準ライブラリ
- オブジェクトとクラス①-クラスの定義-
- オブジェクトとクラス②-継承-
- プロパティ
- ポリモーフィズムとダックタイピング
特殊メソッド
特殊メソッドは、独自で定義したクラスのインスタンス同士の振る舞いを変更することができるメソッド(合ってるかな不安)
よく見るのは __init__() 。インスタンス作成時に呼ばれる特殊メソッド。
この他にも、以下のような特殊メソッドがある。
比較のための特殊メソッド
- __eq__(self, other) - 左辺と右辺が等しいか
- __ne__(self, other) - 左辺と右辺が等しくないか
- __lt__(self, other) - 左辺は右辺より小さい
- __gt__(self, other) - 左辺は右辺より大きい
- __le__(self, other) - 左辺は右辺以下
- __ge__(self, other) - 左辺は右辺以上
算術計算のための特殊メソッド
- __add__(self, other) - 左辺 + 右辺
- __sub__(self, other) - 左辺 - 右辺
- __mul__(self, other) - 左辺 * 右辺
- __floordiv__(self, other) - 左辺 // 右辺
- __truediv__(self, other) - 左辺 / 右辺
- __mod__(self, other) - 左辺 % 右辺
- __pow__(self, other) - 左辺 ** 右辺
上記以外にもありえんくらい特殊メソッドはある。
知りたい場合は、公式ドキュメントへ。
なにはともあれ、コードを書いてみる。
まずは、特殊メソッドを使わない場合。
class UMARU(): def __init__(self, name): self.name = name def equals(self, obj): return self.name == obj.name umaru1 = UMARU("土間埋") umaru2 = UMARU("土間埋")
name属性に格納されている値は同じだが、このままだとインスタンス同士を比較しても比べているのは別々のオブジェクトなので等価ではない。
print(umaru1 == umaru2)
False
name属性同士を比較するには、UMARUクラスに定義したメソッドを使う必要がある。
print(test1.equals(test2))
True
これを特殊メソッドを使って書いてみる。
class UMARU(): def __init__(self, name): self.name = name def __eq__(self, obj): return self.name == obj.name umaru1 = UMARU("土間埋") umaru2 = UMARU("土間埋")
equalsメソッドを__eq__に変えるだけ。
これでオブジェクト同士を比較したときに、きちんとname属性が比較されるようになる。
print(umaru1 == umaru2)
True
少し改良してみると。
class UMARU(): def __init__(self, name, gender): self.name = name self.gender = gender def __eq__(self, obj): if self.gender == obj.gender: return "{}と{}は同性".format(self.name, obj.name) else: return "{}と{}は異性".format(self.name, obj.name) umaru = UMARU("土間埋", "女") taihei = UMARU("土間大平", "男") ebina = UMARU("海老名菜々", "女") print(umaru == taihei) print(taihei == ebina) print(umaru == ebina)
土間埋と土間大平は異性 土間大平と海老名菜々は異性 土間埋と海老名菜々は同性
これと同じ要領で__str__と__repr__も追加してみる。
class UMARU(): def __init__(self, name, gender): self.name = name self.gender = gender def __eq__(self, obj): if self.gender == obj.gender: return "{}と{}は同性".format(self.name, obj.name) else: return "{}と{}は異性".format(self.name, obj.name) def __str__(self): return """ [クラス] UMARU [属性] name = {} gender = {} """.format(self.name, self.gender) def __repr__(self): return "UMARU('{}', '{}')".format(self.name, self.gender)
__str__はクラス自身を出力した際にどういう風に返すかを記述する特殊メソッド(たぶん)。
確認してみる。
print(umaru) print(taihei) print(ebina)
[クラス] UMARU [属性] name = 土間埋 gender = 女 [クラス] UMARU [属性] name = 土間大平 gender = 男 [クラス] UMARU [属性] name = 海老名菜々 gender = 女
そして、よく分からなかったのが __repr__ 。
これは、対話型モードで表示される出力値を決める特殊メソッド(おそらく)。
デバッグ時によく使われてるそう。
確認。
>>> blog_test.umaru UMARU('土間埋', '女')
その他諸々
その他諸々もちょこっとだけ。
オブジェクトの中にオブジェクトを持たせることをコンポジションと呼ぶ。
こんな感じ?
class Face(): def __init__(self, face_point): self.__face_point = face_point @property def face_point(self): return self.__face_point @property def face_str_point(self): return "Face Point: "+str(self.__face_point)+" Point" class Body(): def __init__(self, body_point): self.__body_point = body_point @property def body_point(self): return self.__body_point @property def body_str_point(self): return "Body Point: "+str(self.__body_point)+" Point" class HumanTotal(): def __init__(self, face, body): self.__face = face self.__body = body @property def totalPoint(self): total = (self.__face.face_point + self.__body.body_point) / 2 return "Total Point: "+str(total)+" Point" @property def pointDetail(self): detail = """ {} {} """.format(self.__face.face_str_point, self.__body.body_str_point) return detail
この例では、HumanTotalはFaceとBodyで構成されているという考えのもとで書いてみた。
こういう感じで使われる。
face = Face(7) body = Body(8) human = HumanTotal(face, body) print(human.pointDetail) print(human.totalPoint)
Total Point: 7.5 Point Face Point: 7 Point Body Point: 8 Point
- 名前付きタプル
名前付きタプルは、その名の通りタプル型のデータに名前を付けることができる。
名前でアクセスすることができる。また、通常のタプルのようにインデックスでアクセスすることも可能。
第一引数に名前付きタプル自体の名前、第二引数にデータにアクセスするための名前を渡す。
第二引数は空白で区切っても、以下のコードのように括弧書きで渡しても良い。
import os from collections import namedtuple from pprint import pprint extension = {".py": "Python", ".js":"JavaScript", ".html": "HTML", ".css": "CSS", ".txt": "Text"} files = os.listdir("./") File = namedtuple("File", ("Filename", "Language")) File_List = [] for file in files: file_ex = os.path.splitext(file) try: f = File(file, extension[file_ex[1]]) except KeyError: f = File(file, "不明") File_List.append(f) pprint(File_List)
[File(Filename='basicInfoTest.py', Language='Python'), File(Filename='blog_test.py', Language='Python'), File(Filename='highschool.py', Language='Python'), File(Filename='new_test.py', Language='Python'), File(Filename='randomChoices', Language='不明'), File(Filename='statistics.py', Language='Python'), File(Filename='test.css', Language='CSS'), File(Filename='test.html', Language='HTML'), File(Filename='test.js', Language='JavaScript'), File(Filename='test.txt', Language='Text'), File(Filename='webScraping.py', Language='Python'), File(Filename='__pycache__', Language='不明')]
インデックスでアクセス。
print(File_List[0][0]) print(File_List[0][1])
basicInfoTest.py Python
第二引数に渡した名前でアクセス。
print(File_List[0].Filename) print(File_List[0].Language)
basicInfoTest.py Python
イテラブルなオブジェクトなので、もちろんfor分で回すことができる。
for filename, lang in File_List: print("Filename: ", filename, "| Language: ", lang)
Filename: basicInfoTest.py | Language: Python Filename: blog_test.py | Language: Python Filename: highschool.py | Language: Python Filename: new_test.py | Language: Python Filename: randomChoices | Language: 不明 Filename: statistics.py | Language: Python Filename: test.css | Language: CSS Filename: test.html | Language: HTML Filename: test.js | Language: JavaScript Filename: test.txt | Language: Text Filename: webScraping.py | Language: Python Filename: __pycache__ | Language: 不明
- メソッドのタイプ
クラスメソッド
インスタンスを作らずにアクセスできるメソッド。
第一引数には必ずそのクラスが入る。
class CreateMonster(): __count = 0 def __init__(self): CreateMonster.__count += 1 @classmethod def instance_count(cls): print("Create {} Monster.".format(cls.__count)) CreateMonster.instance_count() monster1 = CreateMonster() CreateMonster.instance_count() monster2 = CreateMonster() CreateMonster.instance_count()
Create 0 Monster. Create 1 Monster. Create 2 Monster.
静的メソッド
こちらもインスタンスを作らずにアクセスできるメソッド。
引数は無くても良い。
class CreateSoldier(): __count = 0 def __init__(self): CreateSoldier.__count += 1 @staticmethod def instance_count(): print("Create {} Soldier.".format(CreateSoldier.__count)) CreateSoldier.instance_count() soldier1 = CreateSoldier() CreateSoldier.instance_count() soldier2 = CreateSoldier() CreateSoldier.instance_count()
Create 0 Soldier. Create 1 Soldier. Create 2 Soldier. Create 2 Soldier.
最後急ぎ足すぎたかな。
色々な人のコード見て事例を学んでいこ。
以上です。
参考にさせていただいたサイト:
Python初心者でも__hoge__使いたい!〜特殊属性,特殊メソッドの使い方〜