Atsushi2022の日記

データエンジニアリングに関連する記事を投稿してます

Pythonでのオブジェクト指向プログラミング

概要

Microsoft LearnにPythonでのオブジェクト指向プログラミングがわかりやすくまとめられていたので、概要をメモしておく。

https://learn.microsoft.com/ja-jp/training/modules/python-object-oriented-programming/

Python を使用したオブジェクト指向プログラミングの概要

オブジェクト指向プログラミング(OOP)でも、手続き型プログラミングでも、プログラムは一連の手順を実行する。つまり、

  1. データ入力
  2. 処理
  3. データ出力

である。違うのは、世界をどのように見るかという点。OOP では、フェーズごとにデータを処理していくのではなく、データが動いている世界全体を把握し、見えている物事を "モデル化" する。

モデル化は以下の手順で行う。

  1. アクションを実行するアクターを特定
  2. アクターの振る舞いを確認
  3. アクションを実行するために必要なデータを特定
  4. アクターをオブジェクトとしてコード化する
    • 特性はオブジェクトのデータとしてコード化する
    • アクターの振る舞いを関数としてオブジェクトに追加する

発想として、オブジェクトのデータは、そのオブジェクトで関数を呼び出すことによって変更される。また、オブジェクトが "相互にやり取りする" ことで、具体的な結果が達成される。

OOPが他のパラダイムと比べて優れているというわけではなく、OOPにも長所と短所がある。OOPの長所として以下の点が挙げられる。

  • データのカプセル化(データへのアクセスを限定)
  • シンプルさ
  • 変更が容易
  • 保守性
  • 再利用性

OOPで理解しておくべき重要な点は以下の通り。

  • クラス、オブジェクト、状態などの基本的な概念と相互関係
  • 変数の取り扱い方法、変数をオブジェクトに追加する方法

オブジェクトとはアクターであり、アクションを実行するもの。アクションが実行されると、アクションを実行したオブジェクトまたは他のオブジェクトの状態が変わる。オブジェクトにはプロパティがある。車ならメーカーやモデル、車種、色など。クラスはオブジェクトのテンプレートである(ブループリントと呼んでもいい)。そして、クラスからオブジェクトが作成される。

クラスからオブジェクトを作成することをインスタンス化(=オブジェクトの作成)といい、これは「クラスに初期値を与え、メモリを確保して、オブジェクトを作成する」ことをOSに要求することである。

オブジェクトには属性(アトリビュート)が付帯されており、属性値はオブジェクトを説明したり、オブジェクトの状態を保持する目的で使用される。

カプセル化

オブジェクト内部のデータを直接確認できなくする、またはデータを変更されないようにすること。つまり、メンバ変数をプライベートにすること。しかし、Pythonだと完璧なプライベート変数は実現できない。

そこで、アンダースコア2個(__)を変数名の前にプレフィクスとしてつけることでプライベートなメンバ変数であることを示す慣習がある。実際、self.__変数名としても外部から変数の値を操作できてしまう。

class Square:
    def __init__(self, val):
          self.__height = val
          self.__width  = val

square = Square(2)
square.__height = 3 # AttributeErrorにならない。Micorosoft LearnだとAttributeErrorを送出すると記載があるが、それは間違い。
print(square.__height) # 3と表示される。

ただ、参照しようとするとちゃんとAttributeErrorになってくれる。

class Square:
    def __init__(self, val):
          self.__height = val
          self.__width  = val

square = Square(2)
print(square.__height) # AttributeErrorになる

ちなみに、_クラス名__変数名とすると、参照もできてしまう。

class Square:
    def __init__(self, val):
          self.__height = val
          self.__width  = val

square = Square(2)
print(square._Square__height) # 2と表示される。エラーにならない

メンバ変数の取り扱い

上記の通り、プライベートなメンバ変数も値を変更できてしまう。そこで、ゲッターとセッターを使用することで、悪い値(想定外の値)を設定できないようにする。

@propertyというデコレーターをメソッドにつけることで()をつけなくてもメソッドが呼び出される。これを利用して値を取得するためのメソッド(ゲッター)を定義する。@<property名>.setterとすることで、値を設定する際に不正な値になっていないかチェックをすることができる。ただし、処理が複雑になるのでむやみにプロパティとして定義するのは避けたほうが良い

class Square:
   def __init__(self, val):
         self.__height = val
         self.__width = val

   def set_side(self, new_side):
         self.__height = new_side
         self.__width = new_side

   @property
   def height(self):
      return self.__height

   @height.setter
   def height(self, new_value):
      if new_value >= 0:
         self.__height = new_value
      else:
         raise Exception("Value must be larger than 0")

OOPにおけるメソッド

OOPではメソッドは、パブリックメソッドとプライベートメソッドにわけられる。パブリックメソッドは他のオブジェクトから呼び出すことができるが、プライベートメソッドは他のオブジェクトから呼び出せない。

Pythonでは、プライベートメソッドは__メソッド名(self)とすることで定義できる。

class Square:
    def __init__(self, val):
        self.__height = val
        self.__width = val
    def __get_area(self):
        return self.__height**2

square = Square(2)
print(square.__get_area()) # AttributeErrorになる

以上