Pythonオブジェクト指向の基本

クラス定義#

クラス定義の構文

classClassName:<statement-1>...<statement-N>

サンプルコード

classDoor:
 def __init__(self, number, status):
  self.number = number
  self.status = status

クラスのインスタンス化#

classDoor:
 def __init__(self, number, status):
  self.number = number
  self.status = status

door =Door(1001,'open')
door.number
door.status

関数の作成と初期化のプロセス

  1. 最初にオブジェクトを作成します
  2. オブジェクトは自己パラメータとして __init__関数に渡されます
  3. 自分を返す

範囲#

クラス変数##

サンプルコード

In [1]:classA:...:         NAME ='A'  #クラスの直属のスコープはクラス変数と呼ばれます
 ...:   def __init__(self, name):...:             self.name = name  #インスタンスに関連付けられた変数は、インスタンス変数と呼ばれます
   ...:          

In [2]: a =A('a')

In [3]: a.NAME
Out[3]:'A'

In [4]: a.name
Out[4]:'a'

In [5]: A.NAME
Out[5]:'A'

In [6]: A.name
---------------------------------------------------------------------------
AttributeError                            Traceback(most recent call last)<ipython-input-6-61c1cc534250>in<module>()---->1 A.name

AttributeError: type object 'A' has no attribute 'name'

In [7]: a2 =A('a2')

In [8]: a2.NAME
Out[8]:'A'

In [9]: a2.NAME ='A2'  #例a2のクラス変数NAMEに値を割り当てます

In [10]: a2.NAME
Out[10]:'A2'

In [11]: a.NAME
Out[11]:'A'

In [12]: A.NAME  #クラス変数に変更はありません
Out[12]:'A'

In [13]: a2.xxx =3

In [14]: a2.xxx  #割り当て後、a2にはさらにxxx属性があります
Out[14]:3

In [15]: A.NAME ='AA'  #クラスのクラス変数を直接変更する

In [16]: A.NAME
Out[16]:'AA'

In [17]: a.NAME  #対応するインスタンスのクラス変数も変更されました
Out[17]:'AA'

In [18]: a2.NAME  #a2のクラス変数は前の割り当てで上書きされたため、クラス変数を変更してもa2には影響しません。
Out[18]:'A2'

など

属性検索順序##

コード

In [1]:classA:...:     NAME ='A'...:     def __init__(self, name):...:         self.name = name
   ...:         

In [2]: a =A('a')

In [3]: a.NAME
Out[3]:'A'

In [4]: a.__class__.NAME
Out[4]:'A'

In [5]: a.__dict__
Out[5]:{'name':'a'}

In [6]: a.__class__  # a.__class__インスタンスに対応するクラスを示します
Out[6]: __main__.A

In [7]: a.NAME ='AA'

In [8]: a.__dict__  #クラス変数をオーバーライドした後__dict__キーと値のペアを追加しました
Out[8]:{'NAME':'AA','name':'a'}

In [9]: a.__dict__['NAME']='AAA'  #直接変更可能__dict__

In [10]: a.__dict__
Out[10]:{'NAME':'AAA','name':'a'}

In [11]: a.__class__.NAME
Out[11]:'A'

In [12]: a.__class__.__dict__
Out[12]:mappingproxy({'NAME':'A','__dict__':<attribute '__dict__'of'A' objects>,'__doc__': None,'__init__':<function __main__.A.__init__>,'__module__':'__main__','__weakref__':<attribute '__weakref__'of'A' objects>})

クラスデコレータ##

パラメータはクラスであり、クラスを返す関数はクラスデコレータにすることができます。

クラスデコレータは通常、クラスに属性を追加するために使用されます。メソッドが追加された場合、それらはすべてクラスレベルのメソッドです。

コード1:クラスに属性を追加

関数メソッドの増加:クラスFにNAME属性を追加するためのset_name関数を定義します

In [1]:classF:...:     pass
   ...: 

In [2]: def set_name(cls, name):  #clsに属性NAMEを追加します=name
 ...:  cls.NAME = name
 ...: return cls
   ...: 

In [3]: F1 =set_name(F,'F')  #F自体を返し、F1はFを指します

In [4]: F1.NAME
Out[4]:'F'

In [5]: f1 =F1()

In [6]: f1.NAME
Out[6]:'F'

In [7]: F1.__dict__
Out[7]:mappingproxy({'NAME':'F','__dict__':<attribute '__dict__'of'F' objects>,'__doc__': None,'__module__':'__main__','__weakref__':<attribute '__weakref__'of'F' objects>})

In [8]: f1.__dict__
Out[8]:{}

In [9]: f1.__class__
Out[9]: __main__.F

In [10]: F.__dict__  #本質的に増加はまだクラスFです
Out[10]:mappingproxy({'NAME':'F','__dict__':<attribute '__dict__'of'F' objects>,'__doc__': None,'__module__':'__main__','__weakref__':<attribute '__weakref__'of'F' objects>})

set_name関数をカレーして、パラメーターを使用してクラスデコレーターを実装します

In [2]: def set_name(name):   #着信パラメータ名
 ...:  def wrap(cls):   #デコレータはラップです
 ...:   cls.NAME = name
 ...: return cls
 ...: return wrap
   ...: 

In [3]: @set_name('G')...:classG:...:     pass
   ...: 

In [4]: G.NAME
Out[4]:'G'

In [5]:classG:...:     pass
   ...: 

In [6]: G =set_name('G')(G)  #デコレータの関数呼び出しメソッド

In [7]: G.NAME
Out[7]:'G'

コード2:クラスにメソッドを追加する

クラスデコレータ get_nameは、クラスHにメソッド __get_name__を追加します

In [1]: def get_name(cls):...:     def _get_name(self):...:return cls.__name__
 ...:  cls.__get_name__ = _get_name  #clsを増やす__get_name__方向_get_name
 ...: return cls
   ...: 

In [2]: @get_name
 ...: classH:...:     pass
   ...: 

In [3]: h =H()

In [4]: h.__get_name__()
Out[4]:'H'

In [5]: H.__dict__
Out[5]:mappingproxy({'__dict__':<attribute '__dict__'of'H' objects>,'__doc__': None,'__get_name__':<function __main__.get_name.<locals>._get_name>,'__module__':'__main__','__weakref__':<attribute '__weakref__'of'H' objects>})

クラスメソッド/静的メソッド##

メソッドの定義はすべてクラスレベルですが、インスタンスを使用して呼び出されるメソッドもあれば、クラスを使用して呼び出されるメソッドもあります。

コード

classI:
 def print(self):  #インスタンス方式
  print('instance method')

 @ classmethod
 def class_print(cls):  #クラスメソッド
  print(id(cls))print('class method')

 @ staticmethod 
 def static_print():  #静的メソッド
  print('static method')

 def xxx_print():  #一般的な方法
  print('this is a function')

アクセス制御#

二重下線##

In [1]:classDoor:...:     def __init__(self, number, status):...:         self.number = number
 ...:   self.__status = status  #二重下線で始まり、二重下線で終わるものはプライベートであり、クラス外からアクセスすることはできません
 ...:  def open(self):...:         self.__status ='opening'...:     def close(self):...:         self.__status ='closed'...:     def status(self):...:return self.__status
 ...:  def __set_number(self, number):  # #ダブルダウンスライドが最初に始まり、ダブルアンダースコアで終わらないメソッドもプライベートです
 ...:   self.number = number
   ...:         

In [2]: door =Door(1001,'closed')

In [3]: door.__status  #プライベートプロパティにアクセスできません
---------------------------------------------------------------------------
AttributeError                            Traceback(most recent call last)<ipython-input-3-d55234f04e7f>in<module>()---->1 door.__status

AttributeError:'Door' object has no attribute '__status'

In [4]: door.__dict__  #ドアオブジェクトに含まれるプロパティ_Door__status
Out[4]:{'_Door__status':'closed','number':1001}

In [5]: door.__status ='hahaha'  #オブジェクトの新しい属性を作成しましたが、変更しませんでした__status

In [6]: door.__status
Out[6]:'hahaha'

In [7]: door.__dict__
Out[7]:{'_Door__status':'closed','__status':'hahaha','number':1001}

In [8]: door.status()
Out[8]:'closed'

In [9]: door.open()

In [10]: door.status()
Out[10]:'opening'

In [11]: door.__set_number(1002)---------------------------------------------------------------------------
AttributeError                            Traceback(most recent call last)<ipython-input-11-888a73f63746>in<module>()---->1 door.__set_number(1002)

AttributeError:'Door' object has no attribute '__set_number'

In [12]: door._Door__status
Out[12]:'opening'

In [13]: door._Door__status ='hehehe'  # _クラス名+二重下線付きの属性の方法でプライベートメンバーを直接変更する

In [14]: door.status()
Out[14]:'hehehe'

単一の下線##

In [1]:classA:...:     def __init__(self):...:         self._a =3...:         

In [2]: a =A()

In [3]: a._a
Out[3]:3

In [4]: a._a =4

In [5]: a._a
Out[5]:4

In [6]: a.__dict__
Out[6]:{'_a':4}

プロパティデコレータ##

プロパティデコレータを紹介します

classDoor:
 def __init__(self, number):
  self.__number = number

 def get_number(self):return self.__number

 def set_number(self, number):
  self.__number = number

number属性がプライベート属性 __numberに変換されると、直接アクセスすることはできません。__number属性には、2つの関数 get_number set_numberを介してのみアクセスできます。

パラメータアクセスを制限できるだけでなく、プロパティのような簡単な方法を使用してクラス変数にアクセスできる場合は、この時点でプロパティデコレータを使用できます。

プロパティデコレータの使用

classDoor:
 def __init__(self, number):
  self.__number = number

 # プロパティデコレータは、自己パラメータのみを持つ関数をプロパティに変換し、プロパティの値はメソッドの戻り値です。
 @ property
 def number(self):return self.__number

 # プロパティセッターデコレータはメソッドを割り当てに変換できますが、このメソッドには特定の要件があります
 # 1. 同名2.自己と値の2つのパラメーターを受け取る必要があります。値は割り当てられた値です
 @ number.setter
 def number(self, number):
  self.__number = number

 @ number.deleter
 def number(self):print('cannot remove number property')

door =Door(1001)
door.number  #1001を返します
door.number =1002
door.number  #1002を返します
del door.number  #出力はnumberプロパティを削除できません

継承#

単一の継承##

classBase:
 PUBLIC_CLASS_VAR ='PUBLIC_CLASS_VAR'
 __ PRIVATE_CLASS_VAR ='PRIVATE_CLASS_VAR'

 def __init__(self):
  self.public_instance_var ='public_instance_var'
  self.__private_instance_var ='private__instance_var'

 @ classmethod
 def public_class_method(cls):return'public_class_method'

 @ classmethod
 def __private_class_method(cls):return'private_class_method'

 @ staticmethod
 def public_static_method():return'public static method'

 @ staticmethod
 def __private_static_method():return'private static method'

 def public_instance_method(self):return'public_instance_method'

 def __private_instance_method(self):return'private_instance_method'classSub(Base):
 pass

sub =Sub()
sub.__dict__
# 出力
{'_ Base__private_instance_var':'private__instance_var','public_instance_var':'public_instance_var'}

メソッドの書き換え##

classBase:
 def __init__(self):
  self.__a =4

 def print(self):print('Base.print')

 @ classmethod
 def cls_print(cls):print('Base.cls_print')classSub(Base):
 def print(self):  ##サブクラスと親クラスに同じ名前のメンバーがある場合、サブクラスのメンバーは同じ名前の親クラスのメンバーをオーバーライドします
  print('Sub.print')

 @ classmethod
 def cls_print(cls):print('Sub.cls_print')

 def foo(self):
  # 親クラスのプリントを呼び出す
  super().print()
  # super(Sub, self).print()

 @ classmethod
 def cls_foo(cls):
  # cls.cls_print()
  # Base.cls_print()super().cls_print()classSubSub(Sub):
 def print(self):print('SubSub.print')

 @ classmethod
 def cls_print(cls):print('SubSub.cls_print')

 def foo(self):
  # ベースのプリントを呼び出す
  super(SubSub, self).print()
  # TYPEの親クラスのメソッドをプロキシし、objを使用して最初のパラメーターをバインドし、呼び出す相手の直接の親を指定します。2番目のパラメーターは、呼び出すときにメソッドの最初のパラメーターとして渡すものを指定します。
  super(Sub, self).print()super(SubSub, SubSub).cls_print()  #クラスメソッドの場合、クラスを渡すことができます。インスタンス自体を渡すこともできます。

 @ classmethod
 def cls_foo(cls):
  # Base.cls_print()super(Sub, cls).cls_print()

複数の継承とMRO

同等のクラス定義###

classA:
 pass

classA(object):
 pass

classA():
 passs

複数の継承###

classA:
 def method(self):print('method of A')classB:
 def method(self):print('method of B')classC(A, B):
 pass

c =C()
c.method()  #Aの出力方法

MRO

次のように、複数の継承を定義します

classA:
 def method(self):print('method of A')classB:
 def method(self):print('method of B')classC(A, B):
 pass

classE(A):
 def method(self):print('method of E')classF(E, A):
 pass

F().method()  #Eの出力方法

定義クラスGが(A、E)から継承する場合、次のようになります。

classG(A, E):  #定義時にエラーを直接報告します
 pass

---------------------------------------------------------------------------
TypeError                                 Traceback(most recent call last)<ipython-input-51-dcac33a3d00c>in<module>()---->1classG(A, E):2     pass

TypeError: Cannot create a consistent method resolution
order(MRO)for bases E, A

エラー表示:ベースE、Aに対して一貫したメソッド解決順序(MRO)を作成できません

メソッド解決順序(MRO)がエラーレポートを満たさない

**基本クラスE、A **のMROを分析します

>>> A.__mro__(__main__.A, object)>>> E.__mro__(__main__.E, __main__.A, object)>>> F.__mro__(__main__.F, __main__.E, __main__.A, object)

したがって、mroシーケンスは継承の順序です

次に、Gクラスのmroシーケンスは(G、A、E、オブジェクト)である必要があります。PythonはC3アルゴリズムを使用して、複数の継承が満たされたときにmroの2つの原則が満たされるかどうかを判断します。

  1. ローカル優先度:自己定義または書き換えられた方法が優先され、継承リストに従って左から右に検索します
  2. 単調性:すべてのサブカテゴリも検索順序を満たす必要があります

C3アルゴリズムの主な機能は、属性が複数の継承でどのクラスに由来するかを判別し、判別できない場合はTypeErrorをスローすることです。

C3アルゴリズム

classB(O):Bのmroシーケンスは次のとおりです。[B, O]classB(A1, A2,..., An):Bのmroシーケンスは次のとおりです。[B]+merge(mro(A1),mro(A2),...,mro(An),[A1, A2,..., An, O])

マージ操作はC3アルゴリズムの中核です。マージ手順は次のとおりです。

* リストをトラバースする
* 最初のリストの最初の要素を見てください
 * 他のリストの最初の要素でもあります
 * または、他のリストに存在しません
* 上記の条件が満たされると、最初の要素が削除され、mroにマージされます。
* 満足できない場合は、例外をスローします

**C3アルゴリズムはクラスF **のmroを分析します

mro(F)->[F]+merge(mro(E),mro(A),[E, A, O])->[F]+merge([E, A, O],[A, O],[E, A, O])->[F, E]+merge([A, O],[A, O],[A, O])->[F, E, A]+merge([O],[O],[O])->[F, E, A, O]

マージ操作は成功し、mroは正しく解析され、最後にmroは[F、E、A、O]になります。

**C3アルゴリズムはクラスG **のmroを分析します

mro(G)->[G]+merge(mro(A),mro(E),[A, E, O])->[G]+merge([A, O],[E, A, O],[A, E, O])-> raise TypeError:

最初のリストの最初の要素はAで、これは2番目のリストには存在しますが、最初の要素ではありません。マージの条件が満たされない場合、例外が直接スローされます。

結論として

  1. 複数の継承を回避するように努める必要があります
  2. 複数の継承は、プログラムの精神的負担に大きな圧力をかけます

Mixinクラス##

参照

  1. [ Liao Xuefeng-複数の継承とMixIn](http://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/0014318680104044a55f4a9dbf8452caf71e8dc68b75a18000)
  2. [ Zhihu-Mixinのコンセプトは何ですか?](https://www.zhihu.com/question/20778853)
  3. [ Pythonクックブック-Mixinsを使用してクラス関数を拡張する](https://python3-cookbook.readthedocs.io/zh_CN/latest/c08/p18_extending_classes_with_mixins.html)

プログラミングでは、mixinは、それから継承されたクラスに追加の関数を提供することを指しますが、それは単独で使用されないクラスです。複数の継承機能を備えたプログラミング言語では、mixinはクラスに関数またはメソッドを追加できます。

したがって、MixInパターンの目的は、クラスに複数の関数を追加することです。このように、クラスを設計するときは、複数レベルの複雑な継承関係を設計するのではなく、複数の継承を通じて複数のMixIn関数を組み合わせることが優先されます。

次の4つのクラスの定義は、Python3.5.2ソースコードsocketserver.pyの639行目から643行目にあります。

classForkingUDPServer(ForkingMixIn, UDPServer): pass
classForkingTCPServer(ForkingMixIn, TCPServer): pass

classThreadingUDPServer(ThreadingMixIn, UDPServer): pass
classThreadingTCPServer(ThreadingMixIn, TCPServer): pass

Pythonには、 TCPServer UDPServerの2種類のネットワークサービスが付属しています。複数のユーザーに同時にサービスを提供するには、マルチプロセスモデルまたはマルチスレッドモデルを使用する必要があります。これら2つのモデルは、 ForkingMixIn ThreadingMixInによって提供されます。組み合わせることにより、上記の4つのカテゴリーが得られます。

これらのカテゴリ間の関係は次のとおりです。

BaseServerからのレイヤーごとの継承のプロセスで、ForkingMixInクラスとThreadingMixInクラスが混在していることがわかります(MixIn)。

この複数の継承の手法は、MixInと呼ばれます。

MixInテクノロジーが使用されていないが、複雑なレベルの単一の継承実装が使用されている場合、クラスの数は指数関数的に増加します。

MixInテクノロジーなしで設計された特定の継承階層関係については、[Liao Xuefeng-複数の継承とMixIn](http://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/0014318680104044a55f4a9dbf8452caf71e8dc68b75a18000)のAnimalクラスの設計アイデアを参照してください。

MixInの概要

MixInは実際には組み合わせの方法です。一般的に言って、構成は継承よりも優れています

Mixinクラスの制限

通常、Mixinクラスは常に継承リストの最初にあります

Recommended Posts

Pythonオブジェクト指向の基本
Pythonの基本
Pythonの基本2
Pythonの基本
Pythonの基本3
Pythonの基本4
Pythonの基本5
Pythonオブジェクト指向の例
Pythonオブジェクト指向の魔法の方法
Pythonカスタム関数の基本
Python構文の基本
Pythonマルチプロセスおよびマルチスレッドの基本
Pythonオブジェクト指向プログラミングの分析
pythonのオブジェクト指向とは何ですか
pythonオブジェクト指向プログラミングを理解する方法
Pythonマルチスレッド
Python CookBook
Python FAQ
Python3辞書
Python3モジュール
Python記述子
Python exec
Python3タプル
Pythonの基礎を学ぶ2日間
Pythonデコレータ
Python IO
Pythonマルチスレッド
Pythonツールチェーン
Python3リスト
Pythonマルチタスク-日常
Pythonの概要
pythonの紹介
Pythonアナリティック
07.Python3関数
Pythonマルチタスクスレッド
Python関数
python演算子
Pythonエントリ-3
Centos 7.5 python3.6
Python文字列
pythonキューキュー