Python-オブジェクト指向

Pythonは、存在して以来、オブジェクト指向言語でした。このため、クラスとオブジェクトの作成と使用は非常に簡単です。この章は、Pythonのオブジェクト指向プログラミングサポートの使用に関するエキスパートになるのに役立ちます。

オブジェクト指向(OO)プログラミングの経験がない場合は、基本的な概念を理解するために、オブジェクト指向(OO)プログラミングの入門コースまたは少なくとも何らかのチュートリアルを参照することをお勧めします。

ただし、ここでは、オブジェクト指向プログラミング(OOP)を簡単に紹介します。

OOP用語の概要

  • Class−クラスの任意のオブジェクトを特徴付ける属性のセットを定義するオブジェクトのユーザー定義のプロトタイプ。属性は、ドット表記を介してアクセスされるデータメンバー(クラス変数とインスタンス変数)とメソッドです。

  • Class variable−クラスのすべてのインスタンスで共有される変数。クラス変数は、クラス内で定義されますが、クラスのメソッドの外部で定義されます。クラス変数は、インスタンス変数ほど頻繁には使用されません。

  • Data member −クラスとそのオブジェクトに関連付けられたデータを保持するクラス変数またはインスタンス変数。

  • Function overloading−特定の機能への複数の動作の割り当て。実行される操作は、関連するオブジェクトまたは引数のタイプによって異なります。

  • Instance variable −メソッド内で定義され、クラスの現在のインスタンスにのみ属する変数。

  • Inheritance −クラスの特性をそのクラスから派生した他のクラスに転送する。

  • Instance−特定のクラスの個々のオブジェクト。たとえば、クラスCircleに属するオブジェクトobjは、クラスCircleのインスタンスです。

  • Instantiation −クラスのインスタンスの作成。

  • Method −クラス定義で定義されている特別な種類の関数。

  • Object−クラスによって定義されたデータ構造の一意のインスタンス。オブジェクトは、データメンバー(クラス変数とインスタンス変数)とメソッドの両方で構成されます。

  • Operator overloading −特定の演算子への複数の関数の割り当て。

クラスの作成

クラスステートメントは、新しいクラス定義を作成します。クラスの名前はすぐにキーワード以下のクラスは次のようにコロン-

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は、自己引数をリストに追加します。メソッドを呼び出すときにそれを含める必要はありません。

インスタンスオブジェクトの作成

クラスのインスタンスを作成するには、クラス名を使用してクラスを呼び出し、その__init__メソッドが受け入れる引数を渡します。

"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

さて、すべての概念をまとめる-

#!/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  # Add an 'age' attribute.
emp1.age = 8  # Modify 'age' attribute.
del emp1.age  # Delete 'age' attribute.

通常のステートメントを使用して属性にアクセスする代わりに、次の関数を使用できます-

  • ザ・ getattr(obj, name[, default]) −オブジェクトの属性にアクセスします。

  • ザ・ hasattr(obj,name) −属性が存在するかどうかを確認します。

  • ザ・ setattr(obj,name,value)−属性を設定します。属性が存在しない場合は、作成されます。

  • ザ・ delattr(obj, name) −属性を削除します。

hasattr(emp1, 'age')    # Returns true if 'age' attribute exists
getattr(emp1, 'age')    # Returns value of 'age' attribute
setattr(emp1, 'age', 8) # Set attribute 'age' at 8
delattr(empl, 'age')    # Delete attribute 'age'

組み込みのクラス属性

すべてのPythonクラスは組み込み属性に従い続け、他の属性と同様にドット演算子を使用してアクセスできます-

  • __dict__ −クラスの名前空間を含む辞書。

  • __doc__ −クラスドキュメント文字列、または未定義の場合はなし。

  • __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':
<function displayCount at 0xb7c84994>, 'empCount': 2, 
'displayEmployee': <function displayEmployee at 0xb7c8441c>, 
'__doc__': 'Common base class for all employees', 
'__init__': <function __init__ at 0xb7c846bc>}

オブジェクトの破壊(ガベージコレクション)

Pythonは、不要なオブジェクト(組み込み型またはクラスインスタンス)を自動的に削除して、メモリスペースを解放します。Pythonが、使用されなくなったメモリのブロックを定期的に再利用するプロセスは、ガベージコレクションと呼ばれます。

Pythonのガベージコレクターはプログラムの実行中に実行され、オブジェクトの参照カウントがゼロに達するとトリガーされます。オブジェクトを指すエイリアスの数が変わると、オブジェクトの参照カウントも変わります。

オブジェクトに新しい名前が割り当てられるか、コンテナ(リスト、タプル、またはディクショナリ)に配置されると、オブジェクトの参照カウントが増加します。オブジェクトの参照カウントは、delで削除された場合、参照が再割り当てされた場合、または参照がスコープ外になると減少します。オブジェクトの参照カウントがゼロに達すると、Pythonはそれを自動的に収集します。

a = 40      # Create object <40>
b = a       # Increase ref. count  of <40> 
c = [b]     # Increase ref. count  of <40> 

del a       # Decrease ref. count  of <40>
b = 100     # Decrease ref. count  of <40> 
c[0] = -1   # Decrease ref. count  of <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) # prints the ids of the obejcts
del pt1
del pt2
del pt3

上記のコードを実行すると、次の結果が得られます-

3083401324 3083401324 3083401324
Point destroyed

Note−理想的には、クラスを別のファイルで定義してから、importステートメントを使用してメインプログラムファイルにインポートする必要があります。

クラスの継承

最初から始める代わりに、新しいクラス名の後に括弧で囲まれた親クラスをリストすることにより、既存のクラスから派生させてクラスを作成できます。

子クラスはその親クラスの属性を継承し、それらの属性を子クラスで定義されているかのように使用できます。子クラスは、親のデータメンバーとメソッドをオーバーライドすることもできます。

構文

派生クラスは、親クラスとほとんど同じように宣言されます。ただし、継承する基本クラスのリストは、クラス名の後に示されています-

class SubClassName (ParentClass1[, ParentClass2, ...]):
   'Optional class documentation string'
   class_suite

#!/usr/bin/python

class Parent:        # define parent class
   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): # define child class
   def __init__(self):
      print "Calling child constructor"

   def childMethod(self):
      print 'Calling child method'

c = Child()          # instance of child
c.childMethod()      # child calls its method
c.parentMethod()     # calls parent's method
c.setAttr(200)       # again call parent's method
c.getAttr()          # again call parent's method

上記のコードを実行すると、次の結果が得られます。

Calling child constructor
Calling child method
Calling parent method
Parent attribute : 200

同様に、次のように複数の親クラスからクラスを駆動できます。

class A:        # define your class A
.....

class B:         # define your class B
.....

class C(A, B):   # subclass of A and B
.....

issubclass()またはisinstance()関数を使用して、2つのクラスとインスタンスの関係を確認できます。

  • ザ・ issubclass(sub, sup) 指定されたサブクラスの場合、ブール関数はtrueを返します sub 確かにスーパークラスのサブクラスです sup

  • ザ・ isinstance(obj, Class)objがクラスClassのインスタンスであるか、Classのサブクラスのインスタンスである場合、ブール関数はtrueを返します。

メソッドのオーバーライド

親クラスのメソッドはいつでもオーバーライドできます。親のメソッドをオーバーライドする理由の1つは、サブクラスに特別な機能または異なる機能が必要な場合があるためです。

#!/usr/bin/python

class Parent:        # define parent class
   def myMethod(self):
      print 'Calling parent method'

class Child(Parent): # define child class
   def myMethod(self):
      print 'Calling child method'

c = Child()          # instance of child
c.myMethod()         # child calls overridden method

上記のコードを実行すると、次の結果が得られます。

Calling child method

基本オーバーロード方法

次の表に、独自のクラスでオーバーライドできるいくつかの一般的な機能を示します。

シニア番号 メソッド、説明、サンプルコール
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クラスを作成したとすると、plus演算子を使用してそれらを追加するとどうなりますか?おそらくPythonはあなたに怒鳴ります。

ただし、クラスで__add__メソッドを定義してベクトルの加算を実行すると、plus演算子は期待どおりに動作します。

#!/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 <module>
      print counter.__secretCount
AttributeError: JustCounter instance has no attribute '__secretCount'

Pythonは、クラス名を含むように名前を内部的に変更することにより、これらのメンバーを保護します。object._className__attrNameなどの属性にアクセスできます。次のように最後の行を置き換える場合、それはあなたのために働きます-

.........................
print counter._JustCounter__secretCount

上記のコードを実行すると、次の結果が得られます。

1
2
2