Python オブジェクト指向

Pythonは設計当初からすでにオブジェクト指向言語です。そのため、Pythonでクラスとオブジェクトを作成するのは非常に簡単です。この章では、Pythonのオブジェクト指向プログラミングについて詳しく説明します。

これまでオブジェクト指向プログラミング言語に触れたことがない場合は、まずオブジェクト指向言語の基本的な特徴をいくつか理解し、頭の中に基本的なオブジェクト指向の概念を形成すると、Pythonのオブジェクト指向プログラミングをより簡単に学習できます。

次に、オブジェクト指向の基本的な特徴を簡単に見ていきましょう。


オブジェクト指向技術の概要

  • クラス(Class):同じ属性とメソッドを持つオブジェクトの集合を記述するために使用されます。その集合内の各オブジェクトが共通して持つ属性とメソッドを定義します。オブジェクトはクラスのインスタンスです。
  • クラス変数:クラス変数は、インスタンス化されたすべてのオブジェクトで共通に使用されます。クラス変数はクラス内で関数本体の外に定義されます。通常、クラス変数はインスタンス変数として使用されません。
  • データメンバー:クラス変数またはインスタンス変数であり、クラスとそのインスタンスオブジェクトに関連するデータを処理するために使用されます。
  • メソッドのオーバーライド:親クラスから継承したメソッドがサブクラスの要件を満たさない場合、それを書き換えることができます。このプロセスはメソッドのオーバーライド(override)と呼ばれ、メソッドの書き直しとも呼ばれます。
  • ローカル変数:メソッド内で定義される変数で、現在のインスタンスにのみ作用します。
  • インスタンス変数:クラスの宣言では、属性は変数で表されます。このような変数はインスタンス変数と呼ばれ、クラス宣言の内部で、クラスの他のメンバーメソッドの外側で宣言されます。
  • 継承:つまり、派生クラス(derived class)が基底クラス(base class)のフィールドとメソッドを継承します。継承により、派生クラスのオブジェクトを基底クラスのオブジェクトとして扱うこともできます。例えば、Dog型のオブジェクトがAnimalクラスから派生する設計は、「is-a(〜は〜である)」関係を模擬しています(例図では、DogはAnimalです)。
  • インスタンス化:クラスのインスタンス、つまりクラスの具体的なオブジェクトを作成すること。
  • メソッド:クラス内で定義された関数。
  • オブジェクト:クラス定義によるデータ構造のインスタンス。オブジェクトには2つのデータメンバー(クラス変数とインスタンス変数)とメソッドが含まれます。

クラスの作成

class 文を使用して新しいクラスを作成します。class の後にはクラス名を置き、コロンで終わります。

class ClassName:
   '类的帮助信息'   #类文档字符串
   class_suite  #类体

クラスのヘルプ情報は ClassName.__doc__ で確認できます。

class_suite はクラスメンバー、メソッド、データ属性で構成されます。

例

以下は簡単なPythonクラスの例です。

例

#!/usr/bin/python # -*- coding: UTF-8 -*- class Employee: 'すべての従業員の基底クラス' 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__() は特別なメソッドで、クラスのコンストラクタまたは初期化メソッドと呼ばれ、このクラスのインスタンスが作成されるときに呼び出されます。

  • self はクラスのインスタンスを表します。self はクラスのメソッドを定義する際に必須ですが、呼び出し時に対応するパラメータを渡す必要はありません。

self はクラスのインスタンスを表し、クラスそのものではありません

クラスのメソッドは通常の関数とただ1つの特別な違いがあります。それらは追加の最初のパラメータ名、慣例によりその名前は self です。

class Test: def prt(self): print(self) print(self.__class__) t = Test() t.prt()

上記の例の実行結果は次のとおりです。

<__main__.Test instance at 0x10d066878>
__main__.Test

実行結果から明らかなように、self はクラスのインスタンスを表し、現在のオブジェクトのアドレスを表します。一方、self.__class__はクラスを指します。

self は Python のキーワードではありません。self を サンプル に置き換えても正常に実行できます。

例

class Test: def prt(サンプル): print(サンプル) print(サンプル.__class__) t = Test() t.prt()

上記の例の実行結果は次のとおりです。

<__main__.Test instance at 0x10d066878>
__main__.Test

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

クラスのインスタンス化では、他のプログラミング言語では通常キーワード new を使用しますが、Python にはこのキーワードはありません。クラスのインスタンス化は関数呼び出しと同様の方式です。

以下では、クラス名 Employee を使用してインスタンス化し、__init__ メソッドを通じてパラメータを受け取ります。

"创建 Employee 类的第一个对象"
emp1 = Employee("Zara", 2000)
"创建 Employee 类的第二个对象"
emp2 = Employee("Manni", 5000)

属性へのアクセス

ドットを使用できます.オブジェクトの属性にアクセスします。次のようにクラス名を使用してクラス変数にアクセスします。

emp1.displayEmployee()
emp2.displayEmployee()
print "Total Employee %d" % Employee.empCount

完全な例:

例

#!/usr/bin/python # -*- coding: UTF-8 -*- class Employee: 'すべての従業員の基底クラス' 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 "Employee クラスの最初のオブジェクトを作成" emp1 = Employee("Zara", 2000) "Employee クラスの2番目のオブジェクトを作成" 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__ : クラスのドキュメント文字列
  • __name__ : クラス名
  • __module__ : クラス定義が置かれているモジュール(クラスの完全名は '__main__.className' です。クラスがインポートされたモジュール mymod 内にある場合、className.__module__ は mymod になります)
  • __bases__ : クラスのすべての親クラスからなる要素(すべての親クラスで構成されるタプルを含む)

Python組み込みクラス属性の呼び出し例は次のとおりです:

例

#!/usr/bin/python # -*- coding: UTF-8 -*- class Employee: 'すべての従業員の基底クラス' 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__: 所有员工的基类
Employee.__name__: Employee
Employee.__module__: __main__
Employee.__bases__: ()
Employee.__dict__: {'__module__': '__main__', 'displayCount': <function displayCount at 0x10a939c80>, 'empCount': 0, 'displayEmployee': <function displayEmployee at 0x10a93caa0>, '__doc__': '\xe6\x89\x80\xe6\x9c\x89\xe5\x91\x98\xe5\xb7\xa5\xe7\x9a\x84\xe5\x9f\xba\xe7\xb1\xbb', '__init__': <function __init__ at 0x10a939578>}

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

Python は、参照カウントという単純な技術を使用してガベージを追跡および回収します。

Python 内部では、使用中のすべてのオブジェクトがそれぞれいくつの参照を持っているかが記録されています。

参照カウンタと呼ばれる内部追跡変数です。

オブジェクトが作成されると参照カウントが作成されます。このオブジェクトが不要になったとき、つまりこのオブジェクトの参照カウントが0になったとき、そのオブジェクトはガベージコレクションされます。ただし、回収は「即時の」ものではなく、インタープリタが適切なタイミングでガベージオブジェクトが占有していたメモリ領域を回収します。

a = 40      # 创建对象  <40>
b = a       # 增加引用, <40> 的计数
c = [b]     # 增加引用.  <40> 的计数

del a       # 减少引用 <40> 的计数
b = 100     # 减少引用 <40> 的计数
c[0] = -1   # 减少引用 <40> 的计数

ガベージコレクション機構は、参照カウントが0のオブジェクトだけでなく、循環参照の場合も処理できます。循環参照とは、2つのオブジェクトが互いに参照しているが、他の変数がそれらを参照していない状態を指します。この場合、参照カウントだけでは不十分です。Python のガベージコレクタは、実際には参照カウンタと循環ガベージコレクタで構成されています。参照カウントの補完として、ガベージコレクタは、割り当てられた総量が非常に大きい(つまり参照カウントによって破棄されなかった)オブジェクトにも注意を払います。この場合、インタープリタは一時停止して、参照されていない循環をすべてクリーンアップしようとします。

例

デストラクタ __del__ は、オブジェクトが破棄されるときに呼び出されます。オブジェクトが使用されなくなったときに、__del__ メソッドが実行されます:

例

#!/usr/bin/python # -*- coding: UTF-8 -*- 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, "破棄" pt1 = Point() pt2 = pt1 pt3 = pt1 print id(pt1), id(pt2), id(pt3) # オブジェクトのidを出力します del pt1 del pt2 del pt3

上記の例の実行結果は次のとおりです。

3083401324 3083401324 3083401324
Point 销毁

注意:通常、クラスは別のファイルで定義する必要があります。

クラスの継承

オブジェクト指向プログラミングの主な利点の1つはコードの再利用であり、この再利用を実現する方法の1つが継承メカニズムです。

継承によって作成された新しいクラスは、次のように呼ばれます。サブクラス或派生クラス、継承されるクラスは次のように呼ばれます。基底クラス、親クラス或スーパークラス。

継承構文

class 派生类名(基类名)
    ...

Pythonにおける継承のいくつかの特徴:

  • 1、サブクラスで親クラスのコンストラクタが必要な場合は、親クラスのコンストラクタを明示的に呼び出すか、親クラスのコンストラクタをオーバーライドしないようにします。詳細は次を参照してください:Python サブクラスが親クラスのコンストラクタを継承する説明。
  • 2、基底クラスのメソッドを呼び出すときは、基底クラスのクラス名プレフィックスを付け、self パラメータ変数を指定する必要があります。クラス内で通常の関数を呼び出すときに self パラメータを指定する必要がない点が異なります。
  • 3、Python は常に対応する型のメソッドを最初に探します。派生クラスで対応するメソッドが見つからない場合にのみ、基底クラス内を1つずつ探し始めます。(最初にこのクラス内で呼び出しメソッドを探し、見つからなければ基底クラスへ探しに行きます。)

継承タプルに複数のクラスがリストされている場合、それは「多重継承」と呼ばれます。

構文:

派生クラスの宣言は親クラスと似ており、継承する基底クラスのリストはクラス名の後に続きます。次のようになります:

class SubClassName (ParentClass1[, ParentClass2, ...]):
    ...

例

#!/usr/bin/python # -*- coding: UTF-8 -*- class Parent: # 親クラスを定義 parentAttr = 100 def __init__(self): print "親クラスのコンストラクタを呼び出し" def parentMethod(self): print '親クラスのメソッドを呼び出し' def setAttr(self, attr): Parent.parentAttr = attr def getAttr(self): print "親クラスの属性:", Parent.parentAttr class Child(Parent): # サブクラスを定義 def __init__(self): print "サブクラスのコンストラクタを呼び出し" def childMethod(self): print 'サブクラスのメソッドを呼び出し' c = Child() # サブクラスをインスタンス化 c.childMethod() # サブクラスのメソッドを呼び出す c.parentMethod() # 親クラスのメソッドを呼び出す c.setAttr(200) # 親クラスのメソッドを再度呼び出す - 属性値を設定 c.getAttr() # 親クラスのメソッドを再度呼び出す - 属性値を取得

上記のコードの実行結果は次のとおりです:

调用子类构造方法
调用子类方法
调用父类方法
父类属性 : 200

複数のクラスを継承できます

class A:        # 定义类 A
.....

class B:         # 定义类 B
.....

class C(A, B):   # 继承类 A 和 B
.....

issubclass() または isinstance() メソッドを使用して検出できます。

  • issubclass() - あるクラスが別のクラスのサブクラスまたは子孫クラスであるかを判定するブール関数。構文:issubclass(sub,sup)
  • isinstance(obj, Class) ブール関数。obj が Class クラスのインスタンスオブジェクト、または Class のサブクラスのインスタンスオブジェクトである場合に true を返します。

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

親クラスのメソッドの機能がニーズを満たさない場合は、サブクラスで親クラスのメソッドをオーバーライドできます:

例:

例

#!/usr/bin/python # -*- coding: UTF-8 -*- class Parent: # 親クラスを定義 def myMethod(self): print '親クラスのメソッドを呼び出し' class Child(Parent): # サブクラスを定義 def myMethod(self): print 'サブクラスのメソッドを呼び出し' c = Child() # サブクラスのインスタンス c.myMethod() # サブクラスがオーバーライドしたメソッドを呼び出す

上記のコードを実行すると、出力結果は次のとおりです:

子クラスのメソッド呼び出し

基本的なオーバーロードメソッド

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

番号メソッド、説明 & 簡単な呼び出し
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)

演算子オーバーロード

Python は演算子のオーバーロードもサポートしています。例は次のとおりです:

例

#!/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)

クラス属性とメソッド

クラスのプライベート属性

__private_attrs:2つのアンダースコアで始まり、その属性がプライベートであることを宣言します。クラスの外部で使用したり直接アクセスしたりすることはできません。クラス内部のメソッドで使用する場合、self.__private_attrs。

クラスのメソッド

クラスの内部で、defキーワードを使用してクラスにメソッドを定義できます。通常の関数定義とは異なり、クラスメソッドはパラメータ self を含む必要があり、それが最初のパラメータです。

クラスのプライベートメソッド

__private_method:2つのアンダースコアで始まり、そのメソッドがプライベートメソッドであることを宣言します。クラスの外部から呼び出すことはできません。クラスの内部で呼び出します。self.__private_methods

例

#!/usr/bin/python # -*- coding: UTF-8 -*- class JustCounter: __secretCount = 0 # プライベート変数 publicCount = 0 # パブリック変数 def count(self): self.__secretCount += 1 self.publicCount += 1 print self.__secretCount counter = JustCounter() counter.count() counter.count() print counter.publicCount print counter.__secretCount # エラーになる。インスタンスはプライベート変数にアクセスできない

Python は名前を変更してクラス名を含めます:

1
2
2
Traceback (most recent call last):
  File "test.py", line 17, in <module>
    print counter.__secretCount  # 报错,实例不能访问私有变量
AttributeError: JustCounter instance has no attribute '__secretCount'

Python はインスタンス化されたクラスがプライベートデータにアクセスすることを許可しませんが、次を使用できます。object._className__attrName( オブジェクト名._クラス名__プライベート属性名)属性にアクセスします。次の例を参照してください:

#!/usr/bin/python
# -*- coding: UTF-8 -*-

class サンプル:
    __site = "www.example.com"

サンプル = サンプル()
print サンプル._サンプル__site

上記のコードを実行すると、実行結果は次のとおりです:

www.example.com

シングルアンダースコア、ダブルアンダースコア、先頭末尾ダブルアンダースコアの説明:

  • __foo__:定義されているのは特別なメソッドで、通常はシステム定義の名前です。例えば__init__()のようなものです。

  • _foo:シングルアンダースコアで始まるものは protected 型の変数を示します。つまり、保護型はそれ自体とサブクラスのみがアクセスを許可され、次の用途には使用できません。from module import *

  • __foo:ダブルアンダースコアで始まるものはプライベート型(private)の変数を示し、そのクラス自体のみがアクセスを許可されます。

その他の拡張