【Python入門】クラスを使う方法!(解説とサンプルコード)

Python Python

Pythonでクラスを使う方法について書いています。
クラスについての解説と、実際にクラスを作っているサンプルコードを載せています。
クラスの解説については、下記のことについて書きました。

・クラスの作り方
・コンストラクタについて
・インスタンス変数を隠す方法
・ゲッター・セッターの実装

載せているサンプルコードはPythonのバージョン3.10.5で確認しました。

Pythonのクラスとは?

クラスはオブジェクト指向で、プログラミングをしていくときに作る設計図のようなものになります。

オブジェクト指向プログラミングでは、モノに注目してプログラムを作成します。
そのモノが何のデータを持っているか、どんな振る舞いをするかなどです。

例えば、車クラスの設計図を使って、インスタンスを作成すると実際に動く車ができるようなイメージです。

Pythonでクラスからインスタンス作成

実際にPythonでクラスをどう実装していくかについて、次項から確認していきましょう。

Pythonでクラスを使うには?

Pythonでクラスを定義する時には、まずclassキーワードでクラスを作成していきます。
下記は、何も実装がない空の車(Car)クラスです。

class Car():
    pass

classを書いた後にスペースを開けて、クラスの名前を書きます。
クラス名は、管理するデータに適した名前を考えてつけます。

その後の丸括弧(())では、継承する親クラスを書きます。今回は継承するクラスがないので空です。
おわりにコロン(:)を書いて、その配下からクラスの実装を書いていきます。

このCarクラスを実際にインスタンス化して使う場合には、下記のようにクラス名に丸括弧(())をつけて呼び出します。
newキーワードを必要とする言語が多いですが、Pythonでは必要ありません。

car = Car()
print(car) # <__main__.Car object at 0x1030a67a0>

コンストラクタを実装する

何の実装もなかったCarクラスにコンストラクタを実装します。
コンストラクタを実装することで、クラスをインスタンス化するときにインスタンスに初期値を持たせることができます。

Pythonのコンストラクタで初期値を持たせる

実際に車種名・色・価格のインスタンス変数を持たせるようにします。
Pythonでコンストラクタを実装する時には__init__というメソッド名で実装します。

class Car():
    def __init__(self, name, color, price):
        self.name = name
        self.color = color
        self.price = price

メソッドを実装しました。
defキーワードを使用して、__init__メソッドを実装しました。
__init__はPythonで決まっている特殊なメソッド名です。

メソッドの引数として、self・name・color・priceを書いています。
一番目のselfは、Pythonでクラスのメソッドを実装する際に、必須な引数になります。
selfは、自分のインスタンスを指すときに使います。名前はself以外でもつけれますが、分かりやすいのでselfがいいかと思います。

2番目からは自分で定義する引数になります。
今回は、車種名(name)と色(color)と価格(price)を渡すようにしました。
インスタンスとして、この3つのデータをインスタンス変数(プロパティ)として保持するようになります。

実際に使う時には、下記のようにインスタンス化します。

my_car = Car("プリウス", "黒", "2597000")
print(f"{my_car.name}は{my_car.color}色で{my_car.price}円です。")

Carクラスを丸括弧(())をつけて呼び出して、引数として、コンストラクタに渡す値を入れています。
これでCarクラスを元にしたmy_carインスタンスができました。

その次の行で、インスタンスのそれぞれのインスタンス変数にアクセスして、インスタンス変数の値を表示しています。
結果「プリウスは黒色で2597000円です。」が出力されます。
インスタンスのコンストラクタに渡した値を保持していることが、確認できました。

他のメソッドを実装する

他のメソッドも実装していきます。
好きなように名前をつけて、defで定義することができます。

先ほどのクラスに対して、showMyCarとrunの2つのメソッドを実装してみました。

class Car():
    def __init__(self, name, color, price):
        self.name = name
        self.color = color
        self.price = price
        self.km = 0

    def showMyCar(self):
        print(f"{self.name}は{self.color}色で{self.price}円です。")

    def run(self, km):
        self.km += km
        print(f'{self.name}で{self.km}km走りました。')

showMyCarメソッドは、先ほどprint関数で出力していた内容をクラスのメソッド化しました。
自分のインスタンスの情報を表示します。

runメソッドは、引数で渡された距離(km)を受け取ると、インスタンス変数のkmに足し込みます。
そして、進んだ距離を出力するというメソッドです。

実装したメソッドを、下記のように使ってみました。

my_car = Car("プリウス", "黒", "2597000")
my_car.showMyCar()

my_car.run(7)
my_car.run(15)
my_car.run(8)

最初にインスタンスに設定した情報が出力されます。
その後に、runメソッドを呼んでいます。渡された引数の距離を足しながら走った距離を表示します。
出力結果は、下記のようになります。

プリウスは黒色で2597000円です。
プリウスで7km走りました。
プリウスで22km走りました。
プリウスで30km走りました。

自身のインスタンス情報を出力した後に、距離が足し込まれながら表示されることが確認できました。

インスタンス変数を隠す

現状だと、車種名(name)と色(color)と価格(price)は、下記のように直接アクセスすることができます。

print(my_car.name) # プリウス
print(my_car.color) # 黒
print(my_car.price) # 2597000

これを直接アクセスできないように、インスタンス変数を隠してみます。

他の言語でいうプライベートな変数になりますが、Pythonでは完全なプライベート変数は存在しません。
これについては、公式のこちらに記載があります。

直接アクセスできないようにするには、インスタンス変数に対して、先頭にアンダーバー(_)をふたつ付けます。
先ほどのCarクラスのインスタンス変数を全て、直接アクセスできないようにしてみました。

class Car():
    def __init__(self, name, color, price):
        self.__name = name
        self.__color = color
        self.__price = price
        self.__km = 0

    def showMyCar(self):
        print(f"{self.__name}は{self.__color}色で{self.__price}円です。")

    def run(self, km):
        self.__km += km
        print(f'{self.__name}で{self.__km}km走りました。')

このようにインスタンス変数の先頭にアンダーバーをふたつ付けると、直接アクセスできなくなります。
下記のように直接アクセスしようとします。

print(my_car.name)

そうすると、このようにエラーが表示されました。

Traceback (most recent call last):
  File "/Users/xxx/Car.py", line 26, in <module>
    print(my_car.name)
AttributeError: 'Car' object has no attribute 'name'

nameがないという内容の属性エラーが表示されました。

これで直接アクセスできないようになりましたが、下記のようにすると、実は値にアクセスすることができます。
なので、完全なプライベート変数がないということですね。

print(my_car._Car__name) # プリウス

ゲッターとセッターを作る

最後にCarクラスに対して、ゲッターとセッターを作ってみます。
ゲッターとセッターを作ることで、バグが発生しずらくなったり、仕様変更に対応しやすくなったりします。

ゲッターとセッターを実装する時には、下記のように@property@[インスタンス変数].setterを使います。

class Car():
    def __init__(self, name, color, price):
        self.__name = name
        self.__color = color
        self.__price = price
        self.__km = 0

    @property
    def name(self):
        return self.__name

    @name.setter
    def name(self, name):
        self.__name = name

    def showMyCar(self):
        print(f"{self.__name}は{self.__color}色で{self.__price}円です。")

    def run(self, km):
        self.__km += km
        print(f'{self.__name}で{self.__km}km走りました。')

車種名(name)の箇所に対してのみ実装しました。
@propertyを付けたメソッドがゲッターメソッドになります。
@name.setterを付けたメソッドがセッターメソッドです。@nameのnameの箇所はインスタンス変数名によって変わります。

ゲッター・セッターを作成したことにより、このようにアクセスすることが可能です。

my_car = Car("プリウス", "黒", "2597000")
print(my_car.name) # プリウス

my_car.name = "カローラ"
print(my_car.name) # カローラ

作成したゲッターのnameにアクセスして、隠していた変数の値が取得できました。
また、セッターにアクセスすることで、値を設定することが可能になりました。

ゲッターを用意しておけば、車種名(name)を返す時の仕様が変わった場合も簡単に対応できます。
例えば、下記のようにすると、先頭に「”車種名:”」という文字列をつけて返すことができるようになります。

@property
def name(self):
    return "車種名:" + self.__name

また、セッターを使った場合は、文字数の制限や空文字を設定させないことが可能です。
下記は設定値が空じゃなくて、5文字以下の場合のみに、値を設定できるようにしています。

@name.setter
def name(self, name):
    if name != '' and len(name) <= 5:
        self.__name = name

こうすることで、想定外の値を設定することを防ぐことができるようになりました。
このようにゲッター・セッターを作ることで、仕様変更に柔軟に対応でき、バグが発生しずらいクラスになりました。

コメント

タイトルとURLをコピーしました