Pythonオブジェクト指向プログラミング完全ガイド:クラスと継承をマスター
OOP用語の概要
- クラス - オブジェクトのユーザー定義プロトタイプ。そのクラスのオブジェクトを特徴づける属性のセットを定義。属性はデータメンバ(クラス変数とインスタンス変数)とメソッドで、ドット記法でアクセス
- クラス変数 - クラスのすべてのインスタンスで共有される変数。クラス内でメソッドの外側に定義。インスタンス変数ほど頻繁には使用されない
- データメンバ - クラスとそのオブジェクトに関連付けられたデータを保持するクラス変数またはインスタンス変数
- 関数オーバーロード - 特定の関数に複数の振る舞いを割り当てること。実行される操作は関与するオブジェクトや引数の型によって異なる
- インスタンス変数 - メソッド内で定義され、そのクラスの現在のインスタンスのみに属する変数
- 継承 - クラスの特徴が、そこから派生した他のクラスに引き継がれること
- インスタンス - 特定のクラスの個々のオブジェクト。例えば、Circleクラスに属するオブジェクトobjは、Circleクラスのインスタンス
- インスタンス化 - クラスのインスタンスを作成すること
- メソッド - クラス定義内で定義される特殊な関数
- オブジェクト - そのクラスによって定義されるデータ構造の一意のインスタンス。オブジェクトはデータメンバ(クラス変数とインスタンス変数)とメソッドの両方で構成
- 演算子オーバーロード - 特定の演算子に複数の関数を割り当てること
クラスの作成
class文は新しいクラス定義を作成します。クラス名はキーワードclassの直後に続き、コロンで終わります。
class ClassName:
'Optional class documentation string'
class_suite
- クラスにはドキュメンテーション文字列があり、
ClassName.__doc__でアクセス可能 class_suiteは、クラスメンバ、データ属性、関数を定義するすべての構成文で構成
例
シンプルなPythonクラスの例:
class Employee:
'Common base class for all employees'
empCount = 0
def __init__(self, name, salary):
self.name = name
self.salary = salary
Employee.empCount += 1
def displayCount(self):
print("Total Employee %d" % Employee.empCount)
def displayEmployee(self):
print("Name : ", self.name, ", Salary: ", self.salary)
- 変数
empCountは、このクラスのすべてのインスタンスで共有されるクラス変数。クラス内部からも外部からもEmployee.empCountとしてアクセス可能 - 最初のメソッド
__init__()は特殊メソッドで、クラスコンストラクタまたは初期化メソッドと呼ばれ、このクラスの新しいインスタンスを作成するときにPythonが呼び出す - 他のクラスメソッドは通常の関数のように宣言するが、各メソッドの最初の引数は
selfである。Pythonはself引数をリストに自動的に追加するため、メソッド呼び出し時に含める必要はない
インスタンスオブジェクトの作成
クラスのインスタンスを作成するには、クラス名を使用してクラスを呼び出し、その__init__メソッドが受け入れる引数を渡します。
"Employeeクラスの最初のオブジェクトを作成"
emp1 = Employee("Zara", 2000)
"Employeeクラスの2番目のオブジェクトを作成"
emp2 = Employee("Manni", 5000)
属性へのアクセス
オブジェクトの属性には、オブジェクトと共にドット演算子を使用してアクセスします。クラス変数はクラス名を使用してアクセスします。
emp1.displayEmployee()
emp2.displayEmployee()
print("Total Employee %d" % Employee.empCount)
すべての概念を組み合わせた完全な例:
#!/usr/bin/python
class Employee:
'Common base class for all employees'
empCount = 0
def __init__(self, name, salary):
self.name = name
self.salary = salary
Employee.empCount += 1
def displayCount(self):
print("Total Employee %d" % Employee.empCount)
def displayEmployee(self):
print("Name : ", self.name, ", Salary: ", self.salary)
"This would create first object of Employee class"
emp1 = Employee("Zara", 2000)
"This would create second object of Employee class"
emp2 = Employee("Manni", 5000)
emp1.displayEmployee()
emp2.displayEmployee()
print("Total Employee %d" % Employee.empCount)
実行結果:
Name : Zara ,Salary: 2000
Name : Manni ,Salary: 5000
Total Employee 2
クラスとオブジェクトの属性は、いつでも追加、削除、変更できます。
emp1.age = 7 # 'age'属性を追加
emp1.age = 8 # 'age'属性を変更
del emp1.age # 'age'属性を削除
通常の文で属性にアクセスする代わりに、以下の関数を使用することもできます。
- getattr(obj, name[, default]) - オブジェクトの属性にアクセス
- hasattr(obj, name) - 属性が存在するかチェック
- setattr(obj, name, value) - 属性を設定。存在しない場合は作成される
- delattr(obj, name) - 属性を削除
hasattr(emp1, 'age') # 'age'属性が存在すればTrueを返す
getattr(emp1, 'age') # 'age'属性の値を返す
setattr(emp1, 'age', 8) # 'age'属性を8に設定
delattr(emp1, 'age') # 'age'属性を削除
組み込みクラス属性
すべてのPythonクラスは以下の組み込み属性を保持し、他の属性と同様にドット演算子でアクセスできます。
- __dict__ - クラスの名前空間を含む辞書
- __doc__ - クラスのドキュメンテーション文字列、未定義ならNone
- __name__ - クラス名
- __module__ - クラスが定義されているモジュール名。インタラクティブモードでは"__main__"
- __bases__ - 基底クラスを含むタプル(空の場合もあり)。基底クラスリストでの出現順序
上記クラスでこれらすべての属性にアクセスしてみます:
#!/usr/bin/python
class Employee:
'Common base class for all employees'
empCount = 0
def __init__(self, name, salary):
self.name = name
self.salary = salary
Employee.empCount += 1
def displayCount(self):
print("Total Employee %d" % Employee.empCount)
def displayEmployee(self):
print("Name : ", self.name, ", Salary: ", self.salary)
print("Employee.__doc__:", Employee.__doc__)
print("Employee.__name__:", Employee.__name__)
print("Employee.__module__:", Employee.__module__)
print("Employee.__bases__:", Employee.__bases__)
print("Employee.__dict__:", Employee.__dict__)
実行結果:
Employee.__doc__: Common base class for all employees
Employee.__name__: Employee
Employee.__module__: __main__
Employee.__bases__: ()
Employee.__dict__: {'__module__': '__main__', 'displayCount': , 'empCount': 2, 'displayEmployee': , '__doc__': 'Common base class for all employees', '__init__': }
オブジェクトの破棄(ガベージコレクション)
Pythonは、不要になったオブジェクト(組み込み型またはクラスインスタンス)を自動的に削除してメモリ空間を解放します。Pythonが使用されなくなったメモリブロックを定期的に回収するプロセスをガベージコレクションと呼びます。
Pythonのガベージコレクタはプログラム実行中に動作し、オブジェクトの参照カウントがゼロになるとトリガーされます。オブジェクトの参照カウントは、それを指すエイリアスの数が変わると変化します。
- オブジェクトが新しい名前で代入されるか、コンテナ(リスト、タプル、辞書)に入れられると参照カウントが増加
delで削除されるか、参照が再代入されるか、参照がスコープ外になると参照カウントが減少- 参照カウントがゼロになると、Pythonが自動的に回収
a = 40 # オブジェクト <40> を作成
b = a # <40> の参照カウント増加
c = [b] # <40> の参照カウント増加
del a # <40> の参照カウント減少
b = 100 # <40> の参照カウント減少
c[0] = -1 # <40> の参照カウント減少
ガベージコレクタが孤立したインスタンスを破棄してそのスペースを回収する瞬間を通常は意識しません。しかし、クラスは特殊メソッド__del__()(デストラクタと呼ばれる)を実装でき、インスタンスが破棄される直前に呼び出されます。このメソッドは、インスタンスが使用していた非メモリリソースのクリーンアップに使用できます。
例
この__del__()デストラクタは、破棄されるインスタンスのクラス名を出力します。
#!/usr/bin/python
class Point:
def __init__(self, x=0, y=0):
self.x = x
self.y = y
def __del__(self):
class_name = self.__class__.__name__
print(class_name, "destroyed")
pt1 = Point()
pt2 = pt1
pt3 = pt1
print(id(pt1), id(pt2), id(pt3)) # オブジェクトのIDを表示
del pt1
del pt2
del pt3
実行結果:
3083401324 3083401324 3083401324
Point destroyed
注 - 理想的には、クラスを別ファイルに定義し、メインプログラムファイルでimport文を使用してインポートすべきです。
クラスの継承
ゼロから始める代わりに、既存のクラスから派生させてクラスを作成できます。新しいクラス名の後に括弧で親クラスを列挙します。
子クラスは親クラスの属性を継承し、それらをあたかも子クラスで定義されたかのように使用できます。子クラスは親からデータメンバとメソッドをオーバーライドすることもできます。
構文
派生クラスは親クラスとよく似ていますが、クラス名の後に継承する基底クラスのリストが続きます。
class SubClassName (ParentClass1[, ParentClass2, ...]):
'Optional class documentation string'
class_suite
例
#!/usr/bin/python
class Parent:
# 親クラスを定義
parentAttr = 100
def __init__(self):
print("Calling parent constructor")
def parentMethod(self):
print('Calling parent method')
def setAttr(self, attr):
Parent.parentAttr = attr
def getAttr(self):
print("Parent attribute :", Parent.parentAttr)
class Child(Parent):
# 子クラスを定義
def __init__(self):
print("Calling child constructor")
def childMethod(self):
print('Calling child method')
c = Child() # 子クラスのインスタンス
c.childMethod() # 子クラスのメソッドを呼び出し
c.parentMethod() # 親クラスのメソッドを呼び出し
c.setAttr(200) # 再び親クラスのメソッドを呼び出し
c.getAttr() # 再び親クラスのメソッドを呼び出し
実行結果:
Calling child constructor
Calling child method
Calling parent method
Parent attribute : 200
同様に、複数の親クラスからクラスを派生させることもできます。
class A:
# クラスAを定義
.....
class B:
# クラスBを定義
.....
class C(A, B):
# AとBのサブクラス
.....
issubclass()やisinstance()関数を使用して、2つのクラスとインスタンスの関係をチェックできます。
- issubclass(sub, sup) - 与えられたサブクラスsubがスーパークラスsupのサブクラスである場合Trueを返すブール関数
- isinstance(obj, Class) - objがクラスClassのインスタンス、またはClassのサブクラスのインスタンスである場合Trueを返すブール関数
メソッドのオーバーライド
親クラスのメソッドをいつでもオーバーライドできます。オーバーライドする理由の一つは、サブクラスで特別なまたは異なる機能を望む場合です。
例
#!/usr/bin/python
class Parent:
# 親クラスを定義
def myMethod(self):
print('Calling parent method')
class Child(Parent):
# 子クラスを定義
def myMethod(self):
print('Calling child method')
c = Child() # 子クラスのインスタンス
c.myMethod() # 子クラスがオーバーライドしたメソッドを呼び出し
実行結果:
Calling child method
基本オーバーロードメソッド
以下の表は、独自のクラスでオーバーライドできる汎用機能の一部を示しています。
| No. | メソッド、説明、サンプル呼び出し |
|---|---|
| 1 | __init__ ( self [,args...] ) - コンストラクタ(オプション引数付き) サンプル呼び出し: obj = className(args) |
| 2 | __del__( self ) - デストラクタ、オブジェクトを削除 サンプル呼び出し: del obj |
| 3 | __repr__( self ) - 評価可能な文字列表現 サンプル呼び出し: repr(obj) |
| 4 | __str__( self ) - 表示可能な文字列表現 サンプル呼び出し: str(obj) |
| 5 | __cmp__ ( self, x ) - オブジェクト比較 サンプル呼び出し: cmp(obj, x) |
演算子のオーバーロード
2次元ベクトルを表すVectorクラスを作成したとします。それらをプラス演算子で加算しようとすると、Pythonはエラーを出すでしょう。
しかし、クラスに__add__メソッドを定義してベクトル加算を実行するようにすれば、プラス演算子が期待通りに動作します。
例
#!/usr/bin/python
class Vector:
def __init__(self, a, b):
self.a = a
self.b = b
def __str__(self):
return 'Vector (%d, %d)' % (self.a, self.b)
def __add__(self, other):
return Vector(self.a + other.a, self.b + other.b)
v1 = Vector(2, 10)
v2 = Vector(5, -2)
print(v1 + v2)
実行結果:
Vector(7,8)
データ隠蔽
オブジェクトの属性は、クラス定義の外部からは見えない場合があります。属性名をダブルアンダースコアで始めると、それらの属性は外部から直接見えなくなります。
例
#!/usr/bin/python
class JustCounter:
__secretCount = 0
def count(self):
self.__secretCount += 1
print(self.__secretCount)
counter = JustCounter()
counter.count()
counter.count()
print(counter.__secretCount)
実行結果:
1
2
Traceback (most recent call last):
File "test.py", line 12, in
print(counter.__secretCount)
AttributeError: JustCounter instance has no attribute '__secretCount'
Pythonはこれらのメンバを、クラス名を含むように内部的に名前を変更することで保護します。このような属性にはobject._className__attrNameとしてアクセスできます。最後の行を以下のように置き換えると動作します。
print(counter._JustCounter__secretCount)
実行結果:
1
2
2
Python
- Raspberry PiとOpenCVによる自動ビジョンベース物体追跡
- C# クラスとオブジェクト:堅牢な OOP の基礎
- Python データ型マスター: 実践ガイド
- Python 演算子完全マスター: 包括的ガイド
- Python pass文完全マスター: 実践ガイド
- Python辞書完全ガイド:作成、操作、高度なテクニック
- Pythonカスタム例外マスター:実践ガイド
- Pythonオブジェクト指向プログラミング完全ガイド:クラス、継承、カプセル化、ポリモーフィズム
- Python継承マスター:概念、構文、実践例
- Python type()とisinstance()の理解:実例付き解説
- Javaシングルトンパターン完全解説:安全な実装と活用法