オブジェクト指向プログラミング (OOP) は、コンピュータプログラムのさまざまな構成要素を、現実世界のオブジェクトになぞらえて考えるプログラミングパラダイムです。
オブジェクトとは、何らかの特性を持ち、機能を果たすことができるものです。
例えば、F1カーレースゲームをオブジェクト指向プログラミングで開発する場合を考えてみましょう。
まず、実際のF1レースに登場するオブジェクトを特定する必要があります。
F1レースにおいて、何らかの特性を持ち、任意の機能を果たすことができる実体は何でしょうか?この問いに対する明白な答えの1つは、車である。
クルマは、エンジン容量、メーカー、車種、製造元などの特徴を持つことができる。
同様に、自動車は発進、停止、加速などが可能である。
ドライバーは、F1レースにおける別の物体である。
ドライバーは国籍、年齢、性別などを持ち、車を運転する、ステアリングを動かす、トランスミッションを変更するなどの機能を実行することができます。
この例のように、オブジェクト指向プログラミングでは、対応する実世界の存在に対してオブジェクトを作成することになる。
ここで重要なのは、オブジェクト指向プログラミングは言語に依存した概念ではないということです。
一般的なプログラミングの概念であり、Java、C#、C++、Pythonなど、現代のほとんどの言語がオブジェクト指向プログラミングをサポートしています。
この記事では、Pythonでのオブジェクト指向プログラミングの詳細な紹介を見ますが、その前に、オブジェクト指向プログラミングの利点と欠点をいくつか見ていきます。
OOPの長所と短所
オブジェクト指向の長所として、以下のようなものがある。
- オブジェクト指向プログラミングは、再利用性を促進する。オブジェクト指向プログラミングは、再利用性を促進します。コンピュータプログラムは、オブジェクトとクラスの形式で記述され、これらは他のプロジェクトでも再利用することができます。
- オブジェクト指向プログラミングでは、モジュール方式を採用しているため、保守性の高いコードになります。
- オブジェクト指向プログラミングでは、すべてのクラスが特定のタスクを持っています。オブジェクト指向プログラミングでは、すべてのクラスが特定のタスクを持ちます。コードの一部でエラーが発生した場合、他の部分に影響を与えることなく、局所的に修正することができます。
- データのカプセル化(後述)は、オブジェクト指向で開発されたプログラムにセキュリティの層を追加するものです。
オブジェクト指向プログラミングには、これまで述べてきたようないくつかの利点がありますが、欠点もいくつかあり、そのいくつかを以下に列挙します。
- オブジェクトを作成するためには、開発するソフトウェアの詳細なドメイン知識が必要である。オブジェクトを作成するためには、開発するソフトウェアに関する詳細な知識が必要である。初心者がこの微妙なラインを見極めるのは難しいかもしれません。
- このような場合、オブジェクトを作成するために必要なのは、「オブジェクトを作成すること」です。
次のセクションでは、オブジェクト指向プログラミングの最も重要な概念のいくつかを見ていきます。
その名の通り、オブジェクト指向プログラミングはオブジェクトがすべてです。
しかし、オブジェクトを作成する前に、そのオブジェクトのためのクラスを定義する必要があります。
クラス
オブジェクト指向プログラミングにおけるクラスは、オブジェクトの設計図のようなものです。
クラスは、家の地図と考えることができます。
地図を見るだけで、その家がどのようなものかを知ることができます。
しかし、クラスそのものは何もない。
例えば、地図は家ではなく、実際の家がどのように見えるかを説明しているに過ぎない。
クラスとオブジェクトの関係は、車とアウディの関係を見てみるとよくわかる。
アウディは実際には車です。
しかし、車だけということはない。
車というのは抽象的な概念であり、実際にはトヨタ、フェラーリ、ホンダなどの形で実装されているのです。
Pythonでクラスを作成するには、キーワード class
を使用します。
クラスの名前は class
キーワードの後に続き、その後にコロン文字が続きます。
クラスの本体は、左からタブ1つ分インデントされた新しい行で開始されます。
それでは、Pythonで非常に基本的なクラスを作成する方法を見てみましょう。
次のコードを見てください。
# Creates class Car
class Car:
# create class attributes
name = "c200"
make = "mercedez"
model = 2008
# create class methods
def start(self):
print ("Engine started")
def stop(self):
print ("Engine switched off")
上の例では、3つの属性を持つ Car
という名前のクラスを作成しています。
name,
make,
modelの3つの属性を持つ
Carというクラスを作成します。
Car クラスは start()
と stop()
という 2 つのメソッドも持っています。
オブジェクト
先に、クラスは青写真を提供すると述べました。
しかし、クラスのオブジェクトやメソッドを実際に使用するには、そのクラスからオブジェクトを作成する必要があります。
オブジェクトがなくても使用できるクラスメソッドや属性はいくつかありますが、それは後のセクションで説明します。
とりあえず、デフォルトでは、クラスのメソッドや属性を使用する前に、そのクラスのオブジェクトを作成する必要があることだけは覚えておいてください。
オブジェクトはインスタンスとも呼ばれ、クラスのオブジェクトを作成することをインスタンス化と呼びます。
Pythonでは、クラスのオブジェクトを作成するには、クラス名の後に開閉括弧を記述するだけです。
前節で作成した Car
クラスのオブジェクトを作成してみましょう。
# Creates car_a object of Car class
car_a = Car()
# Creates car_b object of car class
car_b = Car()
上のスクリプトでは、car クラスのオブジェクトを2つ作成しました。
car_aと
car_bです。
作成したオブジェクトの型を調べるには、type` メソッドにオブジェクトの名前を渡せばよい。
以下のスクリプトを実行してください。
print(type(car_b))
出力では、以下のようになります。
<class '__main__.car'=""
これは、car_b
オブジェクトの型が Car
クラスであることを示しています。
この時点で、クラスとそれに対応するオブジェクトが作成されました。
さて、いよいよクラスオブジェクトを使ってクラスの属性にアクセスしたり、クラスのメソッドを呼び出したりする段階に入りました。
そのためには、オブジェクトの名前にドット演算子をつけて、アクセスしたい属性名や呼び出したいメソッド名をそれぞれ書けばよいのです。
次の例を見てください。
car_b.start()
上のスクリプトでは、car_b
オブジェクトを経由して start()
メソッドを呼び出しています。
出力は以下のようになります。
Engine started
同様に、次のような構文で属性にアクセスすることができます。
print(car_b.model)
出力では、以下のように model
属性の値が表示されます。
2008
属性
前のセクションでは、クラスのオブジェクトを作成し、そのオブジェクトを使用してクラスの属性にアクセスする方法について見てきました。
Pythonでは、すべてのオブジェクトは、ユーザー定義の属性に加えて、いくつかのデフォルトの属性とメソッドを持っています。
オブジェクトのすべての属性とメソッドを見るには、組み込みの dir()
関数を使用します。
前のセクションで作成した car_b
オブジェクトのすべての属性を見ることができるか試してみましょう。
以下のスクリプトを実行してください。
dir(car_b)
出力には、以下の属性が表示されます。
['__class__',
'__delattr__',
'__dict__',
'__dir__',
'__doc__',
'__eq__',
'__format__',
'__ge__',
'__getattribute__',
'__gt__',
'__hash__',
'__init__',
'__init_subclass__',
'__le__',
'__lt__',
'__module__',
'__ne__',
'__new__',
'__reduce__',
'__reduce_ex__',
'__repr__',
'__setattr__',
'__sizeof__',
'__str__',
'__subclasshook__',
'__weakref__',
'make',
'model',
'name',
'start',
'stop']
この組み込み関数は、特にPythonのREPLを介して使用する場合、オブジェクトのすべての属性と関数を検査するのに便利です。
クラス属性とインスタンス属性の比較
属性は大きく分けて2種類に分類されます。
クラス属性とインスタンス属性です。
クラス属性はクラスのすべてのオブジェクトに共有され、インスタンス属性はそのインスタンスの排他的なプロパティです。
インスタンスとは、オブジェクトの別名に過ぎないことを覚えておいてください。
インスタンス属性はメソッドの内部で宣言され、クラス属性はメソッドの外部で宣言されます。
次の例は、この違いを明確にするものです。
class Car:
# create class attributes
car_count = 0
# create class methods
def start(self, name, make, model):
print ("Engine started")
self.name = name
self.make = make
self.model = model
Car.car_count += 1
上のスクリプトでは、クラス属性 car_count
とインスタンス属性 name
、make
、mode
を持つクラス Car
を作成しています。
このクラスには、3 つのインスタンス属性を含むメソッド start()
が 1 つあります。
インスタンス属性の値は start()
メソッドに引数として渡されます。
startメソッドの内部では、
car_count` 属性が 1 つずつ増分されます。
このメソッドの内部では、インスタンス属性は self
キーワードで参照し、クラス属性はクラス名で参照することが重要なポイントになります。
Carクラスのオブジェクトを作成して、
start()` メソッドを呼び出してみましょう。
car_a = Car()
car_a.start("Corrola", "Toyota", 2015)
print(car_a.name)
print(car_a.car_count)
上のスクリプトでは、インスタンス属性 name
とクラス属性 car_count
を出力しています。
出力では、以下のように car_count
属性の値が 1 になることがわかります。
Engine started
Corrola
1
では、car
クラスの別のオブジェクトを作成して、 start()
メソッドを呼び出してみましょう。
car_b = Car()
car_b.start("City", "Honda", 2013)
print(car_b.name)
print(car_b.car_count)
ここで、car_count
属性の値を表示すると、2 が出力されます。
これは、car_count
属性がクラス属性であるため、インスタンス間で共有されるからです。
car_aオブジェクトはその値を 1 に増やし、
car_b` オブジェクトはそれを再び増やしたので、最終的な値は 2 となりました。
出力はこのようになります。
Engine started
City
2
メソッド
先に説明したように、オブジェクト指向プログラミングでは、オブジェクトの機能を実装するためにメソッドを使用します。
前のセクションでは、Car
クラスに start()
と stop()
メソッドを作成しました。
今までは、クラスのオブジェクトを使ってメソッドを呼び出していました。
しかし、クラス名を使って直接呼び出すことができるタイプのメソッドもあります。
このようなメソッドはスタティックメソッドと呼ばれます。
静的メソッド
静的メソッドを宣言するには、以下のようにメソッド名の前に @staticmethod
記述子を指定する必要があります。
class Car:
@staticmethod
def get_class_details():
print ("This is a car class")
Car.get_class_details()
上記のスクリプトでは、クラス Car
を作成し、スタティックメソッド get_class_details()
を一つ追加しています。
このメソッドをクラス名を使って呼び出してみましょう。
Car.get_class_details()
get_class_details()メソッドを呼び出すために、
Car` クラスのインスタンスを作成する必要がなく、単にクラス名を使用していることがわかります。
Pythonでは、静的メソッドはクラスの属性にしかアクセスできないことに注意しましょう。
メソッドから複数の値を返す
Python言語の最も優れた機能の1つは、クラスメソッドが複数の値を返すことができることです。
次の例を見てください。
class Square:
@staticmethod
def get_squares(a, b):
return a*a, b*b
print(Square.get_squares(3, 5))
上のスクリプトでは、Square
というクラスを作り、1つの静的メソッド get_squares()
を用意しました。
このメソッドは2つのパラメータを受け取り、それぞれのパラメータに自分自身を掛けて、 return
文を使って両方の結果を返します。
上のスクリプトの出力には、3と5の二乗が表示されています。
strメソッド
今までは print()
メソッドを使って属性を表示していました。
では、クラスのオブジェクトを表示するとどうなるか見てみましょう。
ここでは、メソッドを一つ持つ簡単なクラス Car
を作成し、そのクラスのオブジェクトをコンソールに出力してみます。
以下のスクリプトを実行してみてください。
class Car:
# create class methods
def start(self):
print ("Engine started")
car_a = Car()
print(car_a)
上のスクリプトでは、Car
クラスのオブジェクト car_a
を生成し、その値を画面に表示しています。
基本的に、ここでは car_a
オブジェクトを文字列として扱っています。
出力はこのようになる。
<__main__.Car object at 0x000001CCCF4335C0>
この出力は、オブジェクトが格納されているメモリの場所を示しています。
Pythonのすべてのオブジェクトはデフォルトで __str__
メソッドを持っています。
オブジェクトを文字列として使用すると、__str__
メソッドが呼び出され、デフォルトではオブジェクトのメモリ上の位置が出力されます。
しかし、__str__
メソッドには独自の定義も用意することができます。
例えば、以下の例を見てください。
# Creates class Car
class Car:
# create class methods
def __str__(self):
return "Car class Object"
def start(self):
print ("Engine started")
car_a = Car()
print(car_a)
上のスクリプトでは、__str__
メソッドに独自の定義をしてオーバーライドしています。
ここで、car_a
オブジェクトをプリントすると、コンソールに “Car class Object” というメッセージが表示されます。
これは、カスタム定義した __str__
メソッドの中で表示されたメッセージです。
このメソッドを使用すると、オブジェクトが表示されたときにカスタムで意味のある説明を作成することができます。
例えば Person
クラスの name
のように、クラス内のデータの一部を表示することもできます。
コンストラクター
コンストラクタは、あるクラスのオブジェクトを作成するときにデフォルトで呼び出される特別なメソッドです。
コンストラクタを作成するには、キーワード __init__
を持つメソッドを作成する必要があります。
次の例を見てください。
class Car:
# create class attributes
car_count = 0
# create class methods
def __init__(self):
Car.car_count +=1
print(Car.car_count)
上のスクリプトでは、クラス属性 car_count
を持つ Car
クラスを作成しています。
このクラスはコンストラクタを持ち、コンストラクタは car_count
の値を増加させ、その結果を画面に表示する。
これで、Car
クラスのオブジェクトが作成されるたびにコンストラクタが呼び出され、car_count
の値がインクリメントされて画面に表示されるようになります。
簡単なオブジェクトを作って、何が起こるか見てみましょう。
car_a = Car()
car_b = Car()
car_c = Car()
オブジェクトを作成するたびに car_count
変数の値がインクリメントされて画面に表示されるため、出力には 1、2、3 の値が表示されます。
名前を除けば、コンストラクタは普通のメソッドとして使うことができます。
コンストラクタから値を渡したり、受け取ったりすることができます。
通常、クラスのインスタンス化時に属性値を初期化したい場合に、このような使い方をします。
ローカル変数とグローバル変数
Pythonの属性には、インスタンス属性とクラス属性の2種類があることが分かっています。
クラスの属性は、変数とも呼ばれます。
スコープによって、変数も2種類に分類されます。
ローカル変数とグローバル変数です。
ローカル変数
クラス内のローカル変数とは、その変数が定義されているコードブロックの内部でのみアクセスできる変数のことです。
例えば、あるメソッドの中で変数を定義した場合、そのメソッドの外からアクセスすることはできません。
次のスクリプトを見てください。
# Creates class Car
class Car:
def start(self):
message = "Engine started"
return message
上のスクリプトでは、ローカル変数 message
を Car
クラスの start()
メソッドの中に作っています。
では、次のように Car
クラスのオブジェクトを作成して、ローカル変数 message
にアクセスしてみましょう。
car_a = Car()
print(car_a.message)
上記のスクリプトは以下のようなエラーを返します。
AttributeError: 'Car' object has no attribute 'message'
これは、ローカル変数が定義されているブロックの外では、ローカル変数にアクセスできないためです。
グローバル変数
グローバル変数は、メソッドやifステートメントなどのコードブロックの外側で定義されます。
グローバル変数は、クラス内のどこにでもアクセスすることができます。
次の例を見てください。
# Creates class Car
class Car:
message1 = "Engine started"
def start(self):
message2 = "Car started"
return message2
car_a = Car()
print(car_a.message1)
上のスクリプトでは、グローバル変数 message1
を作成し、その値を画面に出力しています。
出力では、変数 message1
の値が、エラーなしでプリントされているのがわかります。
ここで重要なのは、クラス属性とインスタンス属性、ローカル変数とグローバル変数に違いがあることです。
クラス属性とインスタンス属性は、クラス名を使用するかインスタンス名を使用するかというアクセス方法の違いです。
一方、ローカル変数とグローバル変数の違いは、そのスコープ、つまりアクセスできる場所にある。
ローカル変数は、メソッドの内部でしかアクセスできません。
この記事では、ローカル変数もインスタンス属性もメソッドの内部で定義していますが、ローカル属性は自己キーワードで定義しています。
アクセスモディファイア
Pythonのアクセス修飾子は、変数のデフォルトのスコープを変更するために使用されます。
Pythonのアクセス修飾子には、public、private、protectedの3種類があります。
publicアクセス修飾子を持つ変数はクラスの内外を問わずどこにでもアクセスでき、private変数はクラスの内部にのみアクセスでき、protected変数は同じパッケージ内にのみアクセス可能です。
プライベート変数を作成するには、変数名の前にアンダースコアを2つ付ける必要があります。
protected 変数を作成するには、変数名の前にアンダースコア 1 つを付けます。
public 変数については、プレフィックスをつける必要はまったくありません。
public変数、private変数、protected変数を実際に使ってみましょう。
次のスクリプトを実行してください。
class Car:
def __init__(self):
print ("Engine started")
self.name = "corolla"
self.__make = "toyota"
self._model = 1999
上のスクリプトでは、コンストラクタと 3 つの変数 name
, make
, model
を持つ、シンプルな Car
クラスを作成しています。
変数 name
は public で、変数 make
と model
はそれぞれ private と protected と宣言されています。
Carクラスのオブジェクトを作成し、変数
name` にアクセスしてみましょう。
以下のスクリプトを実行してください。
car_a = Car()
print(car_a.name)
nameは public 変数なので、クラスの外からアクセスすることができます。
出力では、name`の値がコンソールに表示されます。
次に、変数 make
の値を出力してみましょう。
次のスクリプトを実行してください。
print(car_a.make)
出力には、次のようなエラーメッセージが表示されます。
AttributeError: 'Car' object has no attribute 'make'
ここまでの数節で、オブジェクト指向プログラミングの基本的なコンセプトのほとんどをカバーしました。
次に、オブジェクト指向プログラミングの柱となるものについて説明します。
ポリモーフィズム、継承、カプセル化、これらを総称してPIEと呼びますが、ここではオブジェクト指向プログラミングの柱となるポリモーフィズム、継承、カプセル化について説明します。
継承
オブジェクト指向プログラミングにおける継承は、現実世界の継承とよく似ており、子供は自分独自の特性に加えて、親からいくつかの特性を受け継ぐ。
オブジェクト指向プログラミングでは、継承はIS-Aの関係を意味する。
例えば、自動車は乗り物である。
継承は、コードの再利用性を促進するため、オブジェクト指向プログラミングの最も素晴らしい概念の1つである。
オブジェクト指向プログラミングにおける継承の基本的な考え方は、あるクラスが他のクラスの特性を受け継ぐことができるということです。
他のクラスを継承するクラスは子クラスまたは派生クラスと呼ばれ、他のクラスから継承されるクラスは親クラスまたは基底クラスと呼ばれます。
では、ごく簡単な継承の例を見てみましょう。
次のスクリプトを実行してください。
# Create Class Vehicle
class Vehicle:
def vehicle_method(self):
print("This is parent Vehicle class method")
# Create Class Car that inherits Vehicle
class Car(Vehicle):
def car_method(self):
print("This is child Car class method")
上のスクリプトでは、Vehicle
クラスと、Vehicle
クラスを継承するCar
クラスという2つのクラスを作成しています。
クラスを継承するには、親クラス名を子クラス名に続く括弧の中に記述するだけです。
Vehicleクラスは
vehicle_method()というメソッドを持ち、子クラスは
car_method()というメソッドを持ちます。
しかし、Carクラスは
Vehicleクラスを継承しているので、
vehicle_method()` も継承していることになります。
では、実際に見てみましょう。
以下のスクリプトを実行してください。
car_a = Car()
car_a.vehicle_method() # Calling parent class method
上のスクリプトでは、Car
クラスのオブジェクトを作成し、その Car
クラスのオブジェクトを使って vehicle_method()
を呼び出しています。
Carクラスは
vehicle_method()を持っていませんが、
vehicle_method()を含む
Vehicle` クラスを継承しているので、Car クラスもそれを使用することができます。
出力はこのようになります。
This is parent Vehicle class method
Pythonでは、親クラスは複数の子を持つことができ、同様に、子クラスは複数の親クラスを持つことができます。
最初のシナリオを見てみましょう。
次のスクリプトを実行します。
# Create Class Vehicle
class Vehicle:
def vehicle_method(self):
print("This is parent Vehicle class method")
# Create Class Car that inherits Vehicle
class Car(Vehicle):
def car_method(self):
print("This is child Car class method")
# Create Class Cycle that inherits Vehicle
class Cycle(Vehicle):
def cycleMethod(self):
print("This is child Cycle class method")
上のスクリプトでは、親クラスである Vehicle
は2つの子クラス Car
と Cycle
に継承されます。
どちらの子クラスも親クラスの vehicle_method()
にアクセスすることができます。
以下のスクリプトを実行して、それを確認してください。
car_a = Car()
car_a.vehicle_method() # Calling parent class method
car_b = Cycle()
car_b.vehicle_method() # Calling parent class method
出力では、以下のように vehicle_method()
メソッドの出力が2回表示されます。
This is parent Vehicle class method
This is parent Vehicle class method
親クラスが2つの子クラスに継承されることがわかると思います。
同じように、子クラスは複数の親クラスを持つことができます。
では、例を見てみましょう。
class Camera:
def camera_method(self):
print("This is parent Camera class method")
class Radio:
def radio_method(self):
print("This is parent Radio class method")
class CellPhone(Camera, Radio):
def cell_phone_method(self):
print("This is child CellPhone class method")
上のスクリプトでは、Camera
、Radio
、CellPhone
の3つのクラスを作成しています。
Cameraクラスと
Radioクラスは
CellPhoneクラスに継承されるので、
CellPhoneクラスは
Cameraと
Radio` クラスの両方のメソッドにアクセスできることになります。
以下のスクリプトで確認できます。
cell_phone_a = CellPhone()
cell_phone_a.camera_method()
cell_phone_a.radio_method()
出力はこのようになります。
This is parent Camera class method
This is parent Radio class method
ポリモルフィズム
ポリモーフィズムという言葉は、文字通り複数の形を持つという意味です。
オブジェクト指向プログラミングの文脈では、ポリモーフィズムは、オブジェクトが複数の方法で動作する能力を指します。
プログラミングにおけるポリモーフィズムは、メソッドオーバーローディングとメソッドオーバーライドによって実装されます。
メソッドのオーバーロード
メソッドのオーバーロードとは、パラメータの数や型によってメソッドが異なる動作をする性質を指します。
メソッドのオーバーロードの非常に簡単な例を見てみましょう。
次のスクリプトを実行してください。
# Creates class Car
class Car:
def start(self, a, b=None):
if b is not None:
print (a + b)
else:
print (a)
上のスクリプトでは、1つの引数を渡して start()
メソッドを呼び出すと、そのパラメータが画面に表示される。
しかし、2つの引数を渡して start()
メソッドを呼び出すと、両方の引数を合計して、その結果を画面に表示します。
まずは1つの引数で試してみましょう。
car_a = Car()
car_a.start(10)
出力には、10と表示されます。
次に、引数を2つ渡してみます。
car_a.start(10,20)
出力には、30が表示されます。
メソッドのオーバーライド
メソッドのオーバーライドとは、親クラスと子クラスで同じ名前のメソッドを持つことを指します。
親クラスと子クラスでメソッドの定義は異なりますが、名前は同じままです。
Pythonのメソッドオーバーライドの簡単な例を見てみましょう。
# Create Class Vehicle
class Vehicle:
def print_details(self):
print("This is parent Vehicle class method")
# Create Class Car that inherits Vehicle
class Car(Vehicle):
def print_details(self):
print("This is child Car class method")
# Create Class Cycle that inherits Vehicle
class Cycle(Vehicle):
def print_details(self):
print("This is child Cycle class method")
上のスクリプトでは、Car
と Cycle
クラスが Vehicle
クラスを継承しています。
車両クラスは print_details()
メソッドを持っており、このメソッドは子クラスでオーバーライドされます。
ここで、print_details()
メソッドを呼び出すと、その出力はメソッドが呼び出されているオブジェクトに依存することになります。
次のスクリプトを実行して、この概念を確認してください。
car_a = Vehicle()
car_a. print_details()
car_b = Car()
car_b.print_details()
car_c = Cycle()
car_c.print_details()
出力はこのようになります。
This is parent Vehicle class method
This is child Car class method
This is child Cycle class method
print_details()` メソッドは同じ基底クラスの派生クラスから呼び出されていますが、出力は異なっていることがわかります。
しかし、子クラスは親クラスのメソッドをオーバーライドしているため、メソッドの動作は異なります。
カプセル化
カプセル化は、オブジェクト指向プログラミングの第三の柱である。
カプセル化とは、簡単に言うと、データを隠すことです。
一般的な原則としては、オブジェクト指向プログラミングでは、あるクラスが他のクラスのデータに直接アクセスできるようにすべきではありません。
むしろ、アクセスはクラスメソッドによって制御されるべきです。
Pythonでは、クラスのデータへのアクセスを制御するために、アクセス修飾子とプロパティが使われます。
アクセス修飾子はすでに見てきましたが、この節ではプロパティを実際に見てみましょう。
例えば、車のモデルが常に2000年から2018年の間であることを保証したいとします。
ユーザーが車種に2000より小さい値を入力しようとすると、自動的に2000に設定され、入力された値が2018より大きい場合は、2018に設定されるようにします。
2000と2018の間の値であれば、変更しないようにします。
このロジックを実装したmodel属性のプロパティを以下のように作成します。
# Creates class Car
class Car:
# Creates Car class constructor
def __init__(self, model):
# initialize instance variables
self.model = model
# Creates model property
@property
def model(self):
return self.__model
# Create property setter
@model.setter
def model(self, model):
if model < 2000:
self.__model = 2000
elif model > 2018:
self.__model = 2018
else:
self.__model = model
def getCarModel(self):
return "The car model is " + str(self.model)
carA = Car(2088)
print(carA.getCarModel())
プロパティには3つの部分があります。
まず、属性(上のスクリプトでは model
)が定義されなければならない。
次に、@property デコレータを使用して、属性のプロパティを定義します。
最後に、プロパティ・セッターを作成します。
上のスクリプトでは @model.setter
デスクリプタです。
これで、model 属性に 2018 より大きな値を入力しようとすると、値が 2018 に設定されることが確認できます。
これをテストしてみましょう。
以下のスクリプトを実行します。
car_a = Car(2088)
print(car_a.get_car_model())
ここでは model
の値として 2088 を渡していますが、 get_car_model()
関数で model
属性の値を表示すると、出力には 2018 が表示されます。
結論
この記事では、オブジェクト指向プログラミングの最も重要な概念のいくつかを学びました。
オブジェクト指向プログラミングは、最も有名でよく使われるプログラミングパラダイムの1つです。
オブジェクト指向プログラミングの重要性は、現代のプログラミング言語のほとんどが完全にオブジェクト指向であるか、オブジェクト指向プログラミングをサポートしているという事実にも表れています。
</class