クラス
オブジェクトの構造を定義するための仕組み。
同じクラスを使えば同じ構造をしたオブジェクトをたくさん作りだすことができる。
例えば
str(123)
str(456)
'123'
'456'
このようにstrという組み込み型のクラスを使えば、文字列型の123と456というオブジェクトが生成される
オブジェクト
オブジェクトはデータと処理から構成される。
このデータと処理のことを属性といい、それぞれデータ属性、メソッドと呼ぶ。
クラスオブジェクトとインスタンスオブジェクト
クラス自体のオブジェクト:クラスオブジェクト
クラスから生成されたオブジェクト:インスタンスオブジェクト
オブジェクトの生成
class(引数,)
変数 = class(引数,) # オブジェクトを変数に保存する
メソッドの呼び出し
オブジェクト.メソッド(引数)
# 例
x = set()
x.add(1)
x.add(2)
x
{1,2}
独自クラスの定義
class クラス名:
文
データ属性
インスタンスオブジェクトに値を保存できる仕組み。
オブジェクト.データ属性名
# 例
class Food:
pass
x = Food()
x.name = 'milk'
x.price = '150'
print(x.name , x.price)
milk 150
__init__メソッド
オブジェクトのデータ属性を初期化してくれる
# __init__メソッドの定義
class クラス名:
def __init__(self,引数,...):
文
# データ属性の初期化
self.データ属性名 = 値
例えばさっきのFoodだとデータ属性がリセットされないので__int__メソッドを使って初期化する
class Food:
def __init__(self,name,price):
self.name = name
self.price = price
pass
x = Food('milk',150)
print(x.name , x.price)
y = Food('egg',100)
print(x.name , x.price)
milk 150
egg 200
独自メソッドの定義
# メソッドの定義
class クラス名:
def メソッド名(self,引数,...):
文
これを使えば先ほどのクラスもよりスマートになる
class Food:
def __init__(self,name,price):
self.name = name
self.price = price
def show(self):
print(self.name , self.price)
x = Food('milk',150)
x.show()
y = Food('egg',100)
y.show()
milk 150
egg 200
マングリング
データ処理を外部から隠蔽し、不用意にオブジェクトが変更されることを防止する
class Food:
def __init__(self,name,price):
self.name = name
self.price = price
def show(self):
print(self.name , self.price)
x = Food('milk',150)
x.price //= 2 # データ属性の改変
x.show()
milk 75
こんな感じでデータが書き換わってしまうことがあるのでマングリングで防止する
class Food:
def __init__(self,name,price):
self._name = name # マングリングは_を付ける
self._price = price
def show(self):
print(self._name , self._price)
x = Food('milk',150)
x.price //= 2 # データ属性の改変>エラーになる
x.show()
同じデータ属性だがマングリングすることでデータ属性が改変されないように出来る
クラス属性
クラスのオブジェクトに共通する値を保存するために使用する
class クラス名:
クラス属性名 = 値
# クラス属性の操作
クラス名.クラス属性名
オブジェクト.クラス属性名
class Food:
count = 0
def __init__(self,name,price):
self.name = name
self.price = price
Food.count += 1
def show(self):
print(Food.count , self.name , self.price)
x = Food('milk',150)
x.show()
y = Food('egg',100)
y.show()
1 milk 150
2 egg 200
クラス内部にcountというクラス属性を定義
派生と継承
派生はあるクラスをベースに別のクラスを定義すること。
ベースとなるクラスを基底クラスと呼ぶ。
派生クラスは基底クラスが持つ機能(データ属性とメソッド)を引き継ぐ。
これを継承と呼ぶ。
例題
- Foodオブジェクトを生成
- チョコレートを100円、賞味期限180日で初期化
- Toyオブジェクトを生成
- フィギュア、350円、対象年齢3歳以上で初期化
class Food:
def __init__(self,name,price,use_by_date):
self.name = name
self.price = price
self.use_by_date = use_by_date
def show(self):
print('name:' , self.name)
print('price:' , self.price)
print('use_by_date:' , self.use_by_date)
class Toy:
def __init__(self,name,price,target_age):
self.name = name
self.price = price
self.target_age = target_age
def show(self):
print('name:' , self.name)
print('price:' , self.price)
print('target_age:' , self.target_age)
x = Food('chocolate',100,180)
x.show()
print()
y = Toy('figure',350,3)
y.show()
name: chocolate
price: 100
use_by_date: 180
name: figure
price: 350
target_age: 3
共通する処理が多いので派生して簡略化する
共通する部分を基底クラスとして定義、今回はItemクラスを定義
# 共通する処理を基底クラスでまとめる
class Item:
def __init__(self,name,price):
self.name = name
self.price = price
def show(self):
print('name:',self.name)
print('price',self.price)
# 基底クラスItemを使ってFoodを定義
class Food(Item):
def __init__(self,name,price,use_by_date):
super().__init__(name,price)
self.use_by_date = use_by_date
def show(self):
super().show()
print('use_by_date:',self.use_by_date)
# 基底クラスItemを使ってToyを定義
class Toy(Item):
def __init__(self,name,price,target_age):
super().__init__(name,price)
self.target_age = target_age
def show(self):
super().show()
print('target_age:',self.target_age)
x = Food('chocolate',100,180)
x.show()
print()
y = Toy('figure',350,3)
y.show()
name: chocolate
price 100
target_age: 180
name: figure
price: 350
target_age: 3
super関数
単一継承(一つの基底クラスからの継承)の場合、super関数を使うと基底クラスのメソッドを呼び出すことができる
上記のコードの場合、showメソッドをsuper関数を使って呼び出している
多重継承
先ほどのコードは単一継承
複数の基底クラスを指定すると多重継承になる
例題
先ほどのFoodクラスとToyクラスを基底クラスにする
食品と玩具の機能を持ったShokuganクラスを派生させてみる
この時FoodクラスとToyクラスから派生してShokuganクラスが出来るため、クラスの関係性としてひし形になる
このような継承の関係を菱形継承、ダイヤモンド継承と呼ぶ
# 共通する処理を基底クラスでまとめる
class Item:
def __init__(self,name,price):
self.name = name
self.price = price
def show(self):
print('name:',self.name)
print('price',self.price)
# 基底クラスItemを使ってFoodを定義
class Food(Item):
def __init__(self,name,price,use_by_date):
super().__init__(name,price)
self.use_by_date = use_by_date
def show(self):
super().show()
print('use_by_date:',self.use_by_date)
def eat(self):
print('eating',self.name)
# 基底クラスItemを使ってToyを定義
class Toy(Item):
def __init__(self,name,price,target_age):
super().__init__(name,price)
self.target_age = target_age
def show(self):
super().show()
print('target_age:',self.target_age)
def play(self):
print('playing with',self.name)
# Food/Toyクラスを使ってShokuganを定義
class Shokugan(Food,Toy):
def __init__(self, name, price, use_by_date,target_age):
self.name = name
self.price = price
self.use_by_date = use_by_date
self.target_age = target_age
def show(self):
super().show()
x = Shokugan('chocolate+figure',100,180,3)
x.show()
x.eat()
x.play()
name: chocolate+figure
price 100
target_age: 3
use_by_date: 180
eating chocolate+figure
playing with chocolate+figure
MRO
メソッド解決順序
例えばShokuganクラスは多重継承なので、FoodとToyどちらを先に処理しているのかという問題が発生する
確認方法は以下の通り
# 共通する処理を基底クラスでまとめる
class Item:
def __init__(self,name,price):
self.name = name
self.price = price
def show(self):
print('name:',self.name)
print('price',self.price)
# 基底クラスItemを使ってFoodを定義
class Food(Item):
def __init__(self,name,price,use_by_date):
super().__init__(name,price)
self.use_by_date = use_by_date
def show(self):
super().show()
print('use_by_date:',self.use_by_date)
def eat(self):
print('eating',self.name)
# 基底クラスItemを使ってToyを定義
class Toy(Item):
def __init__(self,name,price,target_age):
super().__init__(name,price)
self.target_age = target_age
def show(self):
super().show()
print('target_age:',self.target_age)
def play(self):
print('playing with',self.name)
# Food/Toyクラスを使ってShokuganを定義
class Shokugan(Food,Toy):
def __init__(self, name, price, use_by_date,target_age):
self.name = name
self.price = price
self.use_by_date = use_by_date
self.target_age = target_age
def show(self):
super().show()
print(Shokugan.__mro__) # MROの確認
(<class '__main__.Shokugan'>, <class '__main__.Food'>, <class '__main__.Toy'>, <class '__main__.Item'>, <class 'object'>)
Shokugan>>Food>>Toy>>Itemの順に処理される
つまりShokuganクラスでsuper().show()を実行するとFoodクラスのshowメソッドを呼び出す。