クラス定義の構文
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
クラス名(最初のパラメーターを除く__init__関数パラメーターリスト)
を使用してオブジェクトを作成します__init__
関数は、オブジェクトの作成時に実際に実行されます__ init__
関数はオブジェクトを作成しません関数の作成と初期化のプロセス
__init__
関数に渡されますサンプルコード
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'
など
__ dict__
:インスタンス変数の辞書__ class__
:インスタンスに対応するクラスを取得します__dict__
を探し、 __class__
を探しますコード
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
を介してのみアクセスできます。
パラメータアクセスを制限できるだけでなく、プロパティのような簡単な方法を使用してクラス変数にアクセスできる場合は、この時点でプロパティデコレータを使用できます。
@ property
デコレータは、メソッドをプロパティ呼び出しに変換する役割を果たしますプロパティデコレータの使用
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()
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の出力方法
次のように、複数の継承を定義します
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つの原則が満たされるかどうかを判断します。
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番目のリストには存在しますが、最初の要素ではありません。マージの条件が満たされない場合、例外が直接スローされます。
結論として:
参照
プログラミングでは、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