0
0

Python学習5日目~クラス~

Last updated at Posted at 2024-01-27

クラス

オブジェクトの構造を定義するための仕組み。
同じクラスを使えば同じ構造をしたオブジェクトをたくさん作りだすことができる。

例えば

example.py
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クラスが出来るため、クラスの関係性としてひし形になる
このような継承の関係を菱形継承ダイヤモンド継承と呼ぶ

example.py
# 共通する処理を基底クラスでまとめる
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どちらを先に処理しているのかという問題が発生する

確認方法は以下の通り

example.py
# 共通する処理を基底クラスでまとめる
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メソッドを呼び出す。

0
0
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0