数弱の文系大学生によるブログです。

Python、Webデザイン関連の記事を投稿していけたらなと思っています。

備忘録的なブログなので、読みにくい文章だと思います。ご了承ください。

Python3の復習(特殊メソッドとその他諸々)

こんにちは。

数学復習中のかにたまごです。
今は微積の復習してます。高校では習ってないかつ数弱なので呑み込みが悪い(;^ω^)
数学に関してはコツコツ基礎を固めていきます。


さてさて、今回は

  • 特殊メソッド
  • その他諸々(コンポジション・名前付きタプル・メソッドのタイプ)

について、復習していきたいと思います。


今までのPython3 復習履歴は以下の通り。

  1. 数値・文字列
  2. リスト・タプル
  3. 辞書・集合
  4. if文・while文・for文
  5. 内包表記
  6. 関数
  7. ジェネレータ
  8. デコレータ
  9. 名前空間とスコープ
  10. アンダーバー・アンダースコアの意味
  11. エラー処理:try・except
  12. モジュール・パッケージ
  13. 標準ライブラリ
  14. オブジェクトとクラス①-クラスの定義-
  15. オブジェクトとクラス②-継承-
  16. プロパティ
  17. ポリモーフィズムとダックタイピング

特殊メソッド

特殊メソッドは、独自で定義したクラスのインスタンス同士の振る舞いを変更することができるメソッド(合ってるかな不安)
よく見るのは __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__使いたい!〜特殊属性,特殊メソッドの使い方〜

【Python入門】クラス利用時の特殊メソッド一覧(サンプルコード付き)

Pythonのstr( )とrepr( )の使い分け

namedtuple(名前付きタプル)で簡易クラスを作る

Python イテレータとイテラブルについて