Remrinのpython攻略日記

python3に入門しました。python3についてあれこれとサンプルコードとか。

クラス

クラスについて。
 

クラスとは

クラス…オブジェクトの設計図のようなもの。
    データとメソッドを持つ。
インスタンス…設計図から作られた実体。オブジェクト
 
・クラス名は大文字で始める。

class Color_test():
    pass

a = Color_test()
print(a)       # <__main__.Color_test object at 0x....>

a.red = 255
print(a.red)   # 255

上記のようにインスタンス作成後に属性(attribute=変数)を追加できるが、
保守性がよくない。
 
・スロット
スロットで、追加できる属性(変数)を制限できる。

class Color_test():
    __slots__ =["green", "red"]
    def __init__(self):
        self.green = 10

a = Color_test()

a.red = 255
print(a.red)   # 255
print(a.green) # 10
a.blue = 0     # AttributeError: 'Color_test' object has no attribute 'blue'

 
インスタンス作成時に1度だけ呼ばれる__init()__ブロックで使用する変数を定義するのが一般的。

class Triangle2():
    def __init__(self, a, b, c):
        self.a , self.b, self.c = a, b, c

t = Triangle2(3, 4, 5)
print(t.a)   # 3
print(t.b)   # 4
print(t.c)   # 5

 

クラス変数とインスタンス変数

クラス変数(クラスアトリビュート)はインスタンスを作成しなくても参照・代入ができる。
インスタンス変数(インスタンスアトリビュート)はインスタンスを作成してからインスタンスごとに参照・代入。
 
クラス変数とインスタンス変数のスコープは、
グローバル変数(モジュール変数)とローカル変数の関係に近い。

モジュールの関数 メソッド
モジュールの変数 クラスのアトリビュート
関数内の変数 インスタンスアトリビュート

 

class Test():
    a = 1        # クラス変数

x =Test()
x.a = 10         # インスタンス変数
print(x.a)       # 10
del x.a          # インスタンス変数を削除すると
print(x.a)       # 1   クラス変数を参照する

 

class Test():
    a = 1                 # クラス変数
    def __init__(self):
        self.b = 2        # インスタンス変数

Test.b = 20   # インスタンスを作らなくてもクラス変数を作れる
print(Test.a) # 1
print(Test.b) # 20

x = Test()
y = Test()

x.a = 5
print(x.a)    # 5   インスタンス変数
print(x.b)    # 2   インスタンス変数 
     
print(y.a)    # 1   インスタンス変数が存在しないときはクラス変数
print(y.b)    # 2   インスタンス変数

Test.a = 100  #     クラス変数を変更
print(x.a)    # 5   インスタンス変数
print(y.a)    # 100 クラス変数

 

selfとは

 クラスのメソッド定義で引数の1つ目は必ずselfにする。
 このselfとはインスタンス自身を引数の1つめにするという意味。

class MyClass():
    def __init__(self, x):
        self.x = x
    def f(self):
        print(self.x)
        
a = MyClass(10) # インスタンス作成

a.f()           # 10
MyClass.f(a)    # 10 最後の2行は同義

この例の最後の行でMyClass.f(a)としているのは、インスタンスaを引数にしている。
これをメソッド定義ではselfとして受け取っている。
下から2行目のa.f()は最後の行のMyClass.f(a)と同義で、
インスタンスaのアトリビュート(属性)fとしてメソッドを呼び出しているが、
このとき最終行と同様にインスタンスaが引数として渡されている。

習慣としてselfを使っている人がほとんどというだけで、他の変数名でもよい。
ただし、保守性が悪くなるのでselfにしよう。
 

dir()

アトリビュートは辞書形式で保存され、dir()で確認できる。

print(dir())  # グローバルのアトリビュート一覧表示
print(dir(x)) # インスタンスxのアトリビュート一覧表示

if "a" in dir(x):
    print("'a' is already assigned")
else:
    print("'a' is not assigned yet")
    
if "A" in dir(x):
    print("'A' is already assigned")
else:
    print("'A' is not assigned yet")

 

メソッド

クラス内に関数を定義するとメソッドとして使える。

import math

# 三角形の面積を求めるクラス
class Triangle2():
    def __init__(self, a, b, c):
        self.a , self.b, self.c = a, b, c
        
    def is_triangle(self):
        return abs(self.b-self.c) < self.a and (self.b+self.c) > self.a

    def area(self):
        if self.is_triangle() != True:
            return 0
        else:
            s = (self.a + self.b + self.c) / 2
            return round((s * (s - self.a) * (s - self.b) * (s - self.c))**0.5, 2)

t = Triangle2(6, 8, 10)
print(t.is_triangle())  # True 
print(t.area())         # 24.0

 
カプセル化
属性やメソッドは変数のように扱えるので書き換えが可能。
整数型を念頭に置いて作った変数に文字列型を代入できたりもする。
意図せぬ動作が起きないように、クラス外からは属性・メソッドを変更しないようにするのが「カプセル化
 
アンダースコア1つを始める → 参照しても、外部から書き換えはしないようにというルール。
アンダースコア2つで始める → クラスの外部からは直接アクセスできなくなる。
 

クラスの継承

親クラス = スーパークラス
子クラス = サブクラス
として、親クラスをひな形にした子クラスを作成できる。
親クラスのメソッドはすべて子クラスで使えるが、変更したいものだけ再定義(オーバーライド)する。

# 正三角形の面積を求めるクラス
class Regular_triangle(Triangle2):
    def __init__(self, length):
        self.a = self.b = self.c = length
        
r = Regular_triangle(5)
print(r.area())          # 10.83

この例では初期化メソッドだけ再定義して、その他のメソッドはすべて親クラスのメソッドを使う。
 
・super()を使って、親クラスのメソッドを呼び出せる。
親クラスの仕様変更に対応しやすくなる。

class Regular_triangle(Triangle2):
    def __init__(self, length):
        # 親クラスの初期化メソッドを呼び出す
        super().__init__(length, length, length) 
        
r = Regular_triangle(5)
print(r.area())         # 10.83

・多重継承
 複数の親クラスから子クラスを作れる。

class MyClass(Super1, Super2, Super3):

継承した属性の探索順は深さ優先で左から右へ。
  

組み込みクラスの継承

組み込みのクラスを継承してカスタマイズできる。
 
インタラクティブシェルでhelp(dict)とするとdictクラスのメソッドを確認できる。
dictクラスを継承し、キーとして文字列だけを指定できるようにカスタマイズ。

# dictクラスを継承
class StrDict(dict):                    
    # 書き換えたいメソッドのみを上書きする。
    def __setitem__(self, key, value):
        if not isinstance(key, str):
            raise ValueError("Key must be str.")
        super().__setitem__(key, value)
        
d1 = StrDict()
d1["red"] = "0xFF0000"
print(d1["red"])

#d1[0] = 0  # ValueError: Key must be str.

親クラスの__setitem__()メソッドを呼び出すときは、
super().__setitem__(key, value)
super(StrDict, self).__setitem__(key, value)
dict.__setitem__(self, key, value)
とか。
 

インスタンスの型チェック

左側のオブジェクトは右側のクラスまたはその派生クラスのインスタンスか。

print(isinstance("1.23", float))  # False
print(isinstance(1.23, float))    # True
print(isinstance("1.23", object)) # True

クラスの継承チェック

左側のクラスは右側のクラスのサブクラスか。

print(issubclass(int, object))    # True 

すべての型はobject型からの派生(object型を継承)
 

アンダーバーによる変数の隠蔽

変数名の左側にアンダーバー2つで、右側にアンダーバーなしまたは1つのとき、
その変数は外部からのアクセスにAttributeErrorを返す。

class MyClass():
    def __init__(self):
        self._a = -1
        self.__b = 0
        self.__c_ = 1
        self.__d__ = 2
        self.__e___ = 3

x = MyClass()
print(x._a)      # -1
#print(x.__b)    # AttributeError   
#print(x.__c_)   # AttributeError
print(x.__d__)   # 2
print(x.__e___)  # 3    

 
上記の方法でのアクセスではAttributeErrorになるが、
インスタンス名._クラス名__属性名でアクセスできる。

print(x._MyClass__b)  # 0

 

特殊メソッド

組み込みクラスでよく使われている特殊メソッド。
組み込みクラスを継承するときは必要に応じて書き換える。

__add__(self, obj) +演算子使用時に呼ばれる
__iadd__(self, obj) *=
__sub__(self, obj) -
__mul__(self, obj) *
__truediv__(self, obj) /
__floordiv__(self, obj //

 

__and__(self, obj) &演算子使用時
__or__(self, obj)
__eq__(self, obj)  ==
__ne__(self, obj)  !=
__lt__(self, obj)  < 
__le__(self, obj)  <=
__gt__(self, obj)  >
__ge__(self, obj)  >=

__cmp()__メソッドはpython3では使わない。
 

__int__(self) int()関数使用時
__float__(self) float()
__str__(self) str()、print()関数
__repr__(self) 文字列表記へ変換、インタラクティブシェルへ表示。文字列なら' 'で囲うとか。
__bytes(self) bytes()
__format(self) format()

 

__len__(self) len()
__getitem(self, key) [ ]で要素指定するとき
__setitem(self, key) 代入
__delitem(self, key) 削除
__iter(self) iter()関数の呼び出し時
__contains__(self, item) in演算子

 

__getattr(self, attr) 未定義属性の参照時
__getattribute__() すべての属性参照
__setattr(self, attr, item) 属性への代入時

 

__call__(self[, args]) オブジェクトを関数として呼び出し
__del__(self) デストラクタ(オブジェクトの破棄)
__hash(self) set、dict型の要素定義など、オブジェクトのhash値が必要になるとき。