Pythonパート2オブジェクトシステムに関する注意

Pythonオブジェクトシステムに関するメモの2番目の部分(最初の部分はこちら )。 この記事では、クラス、メタクラス、タイプ、オブジェクトとは何か、そしてクラス内で属性を検索する方法について説明します。


クラス


クラス(タイプ)はオブジェクトファクトリです。 それらの主なタスクは、特定の動作を持つオブジェクトを作成することです。

クラスは、属性(__dict__クラスに格納されている)を使用してオブジェクトの動作を定義します:メソッド、プロパティ、クラス変数、記述子、および親クラスから継承された属性を使用します。

通常のオブジェクトは、最初に作成、次に初期化の2段階でインスタンス化されます。 したがって、最初に__new__クラスメソッドが起動され、このクラスのオブジェクトが返されます。次に、__ init__クラスメソッドが実行され、既に作成されたオブジェクトが初期化されます。

def __new __(cls、...)は、クラスclsのオブジェクトを作成する静的メソッドです(ただし、そのように宣言することはできません)。

def __init __(self、...)は、作成されたオブジェクトを初期化するクラスメソッドです。

たとえば、クラスを宣言します。

>>> class A ( object ):
... pass
...
>>>


クラスAの場合、__ new__も__init__も定義されていません。 クラス(タイプ)の属性検索アルゴリズム(通常のオブジェクトの属性検索アルゴリズムと混同しないでください)に従って、クラスが__dict__でそれらを見つけられない場合、ベース(親)クラスの__dict__でこれらのメソッドを探します。

クラスAには、親として組み込みクラスオブジェクトがあります。 したがって、彼はオブジェクトでそれらを探します.__ dict__

そして見つけます:

>>> object . __dict__[ '__init__' ]
<slot wrapper '__init__' of 'object' objects>
>>> object . __dict__[ '__new__' ]
<built-in method __new__ of type object at 0x82e780>
>>>


このようなメソッドがあるため、a = A()は一連の呼び出しに似ていることを意味します。

a = object .__ new __(A)
オブジェクト.__ init __(a)

一般的には、親クラスによる属性の検索アルゴリズムを実装するスーパーを使用します[1]:

a = super(A、A).__ new __(A)
super(A、A).__ init __(a)

例。

>>> class A ( object ):
... def __new__ (cls):
... obj = super (A, cls) . __new__(cls)
... print 'created object' , obj
... return obj
... def __init__ ( self ):
... print 'initing object' , self
...

>>> A()
created object <__main__.A object at 0x1620ed0>
initing object <__main__.A object at 0x1620ed0>
<__main__.A object at 0x1620ed0>
>>>


シングルトンv。1


オブジェクトの作成方法を理解すると、孤立パターンの実装を記述できます。

クラスにインスタンスが1つだけあることを確認する必要があります。 つまり クラスのコンストラクターを呼び出すとき、常にクラスの同じインスタンスを返します。

そして、これは__new__メソッドを呼び出すとき、毎回同じオブジェクトを返さなければならないことを意味します。 オブジェクト自体を、たとえばクラス変数インスタンスに保存できます。

その結果、以下が得られます。

>>> class C ( object ):
... instance = None
... def __new__ (cls):
... if cls . instance is None :
... cls . instance = super (C, cls) . __new__(cls)
... return cls . instance
...
>>> C() is C()
True
>>> C() . x = 1
>>> c = C()
>>> d = C()
>>> c . x
1
>>> d . x
1
>>> c . x =2
>>> d . x
2
>>> c . x
2


クラスとメタクラス。


クラス(タイプ)と通常のオブジェクトには、クラスを作成し、クラスの動作を定義するクラス(タイプ)があります。 このクラスはメタクラスと呼ばれます。

通常のオブジェクトのようにクラスを作成するには、コンストラクターを呼び出しますが、 クラスには初期化する必要のあるいくつかの追加の特別な属性があり、対応する必須パラメーターがコンストラクターに渡されます。

XClass = XMetaClass(名前、ベース、attrs)

次に、作成直後
XClass .__ name__はnameと等しい、
XClass .__ bases__はbasesと等しい、
XClass .__ dict__はattrsと等しい
XClass .__ class__はXMetaClassと等しい

デフォルトでは、定義されたすべてのクラスのメタクラスはタイプです。

そのように。

>>> class A ( object ):
... pass
...


同様に、通常のオブジェクトとの類推によって:

>>> type ( 'A' , ( object ,), {})
<class '__main__.A'>


そしてこれ:

>>> class B (A):
... def foo ( self ):
... 42
...


同等に

>>> type ( 'B' , (A,), { 'foo' : lambda self : 42 })

クラスを定義するとき、次を使用してメタクラスを指定できます。
クラス変数__metaclass__:

>>> class A ( object ):
... __metaclass__ = Meta
...
>>>


これは同等です:A = Meta( 'A'、(object、)、{})

タイプとオブジェクトについて


まず、タイプとオブジェクトはオブジェクトです。 そして、すべてのまともなオブジェクトのように、それらは特別な属性__class__と__dict__を持っています:

>>> object . __class__
<type 'type'>
>>> type . __class__
<type 'type'>


>>> object . __dict__
<dictproxy object at 0x7f7797a1cf30>
>>> type . __dict__
<dictproxy object at 0x7f7797a1cfa0>


さらに、オブジェクトとタイプはタイプオブジェクト(クラス)であり、特別な属性__name __、__ bases___も持っています。

>>> object . __name__
'object'
>>> type . __name__
'type'
>>> object . __bases__
()
>>> type . __bases__
(<type 'object'>,)
>>>


タイプまたはクラスオブジェクトのインスタンスはオブジェクト(任意)です。 つまり 任意のオブジェクトはオブジェクトクラスのインスタンスです。

>>> isinstance ( 1 , object )
True
>>> isinstance ( setattr , object )
True
>>> isinstance ( "foo" , object )
True
>>> isinstance (A, object )
True


関数でもオブジェクトです:
>>> def bar ():
... pass
...
>>> isinstance (bar, object )
True


さらに、オブジェクトクラス自体がそのインスタンスです。

>>> isinstance ( object , object )
True


typeはそのインスタンスでもあります:

>>> isinstance ( type , object )
True


インスタンス化-object()は、最も単純で最も一般的なオブジェクトを返します。

>>> o = object ()

__dict__もありません。__class__しかありません。

>>> o . __dict__
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'object' object has no attribute '__dict__'
>>> o . __class__
<type 'object'>
>>>


クラスまたは型のインスタンスは、他のクラスまたは他の型にすぎません。

数字はクラスではありません

>>> isinstance ( 1 , type )
False


文字列も

>>> isinstance ( "foo" , type )
False


組み込みのsetattr関数もクラスではありません。

>>> isinstance ( setattr , type )
False


クラスはクラスです。

>>> isinstance (A, type )
True


文字列型はクラスです。

>>> isinstance ( "foo" . __class__, type )
True


なぜなら オブジェクトとタイプもクラスであり、それらはタイプクラスのインスタンスです。

>>> isinstance ( object , type )
True
>>> isinstance ( type , type )
True
>>>


なぜなら 多くのクラス(タイプ)は多くのオブジェクトのサブセットであるため、タイプがオブジェクトのサブクラスであると仮定することは論理的です。

>>> issubclass ( type , object )
True
>>> issubclass ( object , type )
False


typeは、インスタンスが他のクラスである単なるクラスです。 (つまり、メタクラス)。 また、クラス自体は、単純な通常のオブジェクトの拡張と考えることができます。

したがって、オブジェクトからクラスを継承すると、このクラスはオブジェクトクラスの動作を自動的に継承します。 インスタンス化すると、通常のオブジェクトが返されます。 そして、型クラスから継承するとき、型クラスの動作も自動的に継承します。 インストール時に、クラスが作成されます。 そして、クラスを作成するクラスはメタクラスと呼ばれます。

したがって、クラスだけを定義するには、オブジェクトからそれを継承し、メタクラスを定義する必要があります-型から継承します。

そしてもう1つ:タイプ(a)とタイプ(name、bases、attrs)を混同しないでください。
type(a)-1つの引数を持つ呼び出しは、オブジェクトのタイプを返します。
タイプ(名前、ベース、attrs)-3つの引数を持つ呼び出し-これはクラスコンストラクターの呼び出しです。

クラス内の属性の検索について


既に述べたように、通常のオブジェクトの属性を検索するアルゴリズムですが、いくつかの微妙な点があります。 タイプ(クラス)には__bases__-親クラス(タイプ)があります。

属性が__dict__に存在する場合、その属性が返され、__ bases__から基本クラスで検索が実行され、その後__dict__ __class __ 'a(つまり、実際にはメタクラス)とその(メタクラス)親クラス(メタクラス)が呼び出されます。

小さな例:

>>> class Ameta ( type ):
... def foo (cls):
... print 'Ameta.foo'
...
>>> class A ( object ):
... __metaclass__ = Ameta
...
>>> A . foo()
Ameta.foo


メタクラスで定義されているものはすべてクラスで利用できますが、クラスのインスタンスでは利用できません—通常のオブジェクト 属性は、__ dict__クラスの辞書でのみ通常のオブジェクトで検索されます。

>>> a = A()
>>> a . foo
Traceback (most recent call last):
File "<stdin>" , line 1 , in <module>
AttributeError : 'A' object has no attribute 'foo'


Aでは.__ dict__ 'foo'はそうではありません:

>>> A . __dict__[ 'foo' ]
Traceback (most recent call last):
File "<stdin>" , line 1 , in <module>
KeyError : 'foo'


ただし、メタクラスに含まれているため、次のようになります。

>>> A . foo()
Ameta.foo

>>> class B (A):
... @classmethod
... def foo (cls):
... print 'B.foo'
...
>>> B . foo # .. foo B.__dict__ B.__dict__['foo']
<bound method Ameta.foo of <class '__main__.B'>>
>>> B . foo()
B.foo

>>> class C (B):
... pass
...
>>> C . foo() # B.
B.foo

C foo B.

>>> c = C()
>>> c . foo()
B.foo

>>> class D (A):
... pass
...
>>> D . foo()
Ameta.foo


また、インスタンスDは次を見つけません。

>>> d = D()
>>> d . foo()
Traceback (most recent call last):
File "<stdin>" , line 1 , in <module>
AttributeError : 'D' object has no attribute 'foo'


メタクラス


メタクラスは、クラス(または型)のファクトリです。 クラスのインスタンス化は、2つの段階(タイプ(クラス)のオブジェクトの作成と初期化)でも行われます。 これも2つのメタクラスメソッドを使用して行われます。 最初に、メタクラスの__new__メソッドが、クラスの作成に必要なパラメーター(名前、ベース、attrs)で呼び出され、その後、同じパラメーターと既に作成されたクラスで__init__が呼び出されます。

例。

>>> class Meta ( type ):
... pass
...
>>> Meta( 'A' , ( object ,), {})
<class '__main__.A'>


最初に、メタクラスMetaは__dict__辞書で__new__メソッドを検索しますが、そこでは見つかりません。また、__ dict__の親クラス(つまり、この場合はメタクラス)の検索を開始します。 クラス内の通常の属性検索が発生します。 適切なパラメーターを使用して__new__を実行した結果、新しいクラスを受け取り、メタクラスの__init__メソッドを呼び出して初期化します。

完全に展開されたフォームでは、次のことがわかります。

cls = type .__ dict __ ['__ new __'](Meta、 'A'、(object、)、{})
タイプ.__ dict __ ['__ init __'](cls、 'A'、(object、)、{})

またはスーパーを使用して

cls = super(メタ、メタ).__ new __(メタ、 'A'、(オブジェクト、)、{})
super(メタ、メタ).__ init __(cls、 'A'、(object、)、{})

通常のオブジェクトのインスタンス化とは異なり、オブジェクト.__ new__およびオブジェクト.__ init__ではなく、タイプ.__ new__およびタイプ.__ init__が使用されることに注意してください。 オブジェクト.__ new__とタイプ.__ new__は異なるシグネチャを持ち、オブジェクト.__ new__は通常のオブジェクトを返し、タイプ.__ new__はタイプ(typeobject)のオブジェクトです。 クラス。

これがすべてどのように機能するかを例で見てみましょう。

>>> class Meta ( type ):
... def __new__ (mcls, name, bases, attrs):
... print 'creating new class' , name
... return super (Meta, mcls) . __new__(mcls, name, bases, attrs)
... def __init__ (cls, name, bases, attrs):
... print 'initing new class' , name
...
...
>>> class A ( object ):
... __metaclass__ = Meta
...
creating new class A
initing new class A


オブジェクトのみのインスタンス化中、ラベルは表示されません。

>>> a = A()
>>>

さらに、メタクラスの__new__および__init__メソッドでは、それぞれ名前、スーパークラスのリスト、属性のすべてを変更できます。

リンク集




注釈


[1]スーパーの詳細はこちら

続きを読む


Pythonオブジェクトシステムに関するパート3

Source: https://habr.com/ru/post/J114585/


All Articles