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

Pythonオブジェクトシステムに関するいくつかの注意事項。 Pythonでのプログラミング方法をすでに知っている人向けに設計されています。 それは、Python 2.3以降の新しいクラス(新しいスタイルのクラス)についてのみです。 この記事では、オブジェクトとは何か、属性の検索方法について説明します。


オブジェクト


Pythonのすべてのデータはオブジェクトです。 各オブジェクトには、2つの特別な属性__class__および__dict__があります。
例。

>>> def foo (): pass
...
>>> foo . __class__
<type 'function'>
>>> foo . __dict__
{}
>>> ( 42 ) . __dict__
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'int' object has no attribute '__dict__'
>>> ( 42 ) . __class__
<type 'int'>
>>> class A ( object ):
... qux = 'A'
... def __init__ ( self , name):
... self . name = name
... def foo ( self ):
... print 'foo'
...
>>> a = A( 'a' )

Aには__dict__と__class__もあります:

>>> a . __dict__ {'name': 'a'}
>>> a . __class__
<class '__main__.A'>
>>> type (a)
<class '__main__.A'>
>>> a . __class__ is type (a)
True


クラスとタイプはまったく同じです。

>>> a . __class__ is type (a) is A
True


a .__ dict__は、内部(またはオブジェクト固有の)属性(この場合は 'name')を含む辞書です。 .__ class__クラス(タイプ)。

また、たとえば、クラスメソッドでは、self.foo = barの割り当てはself .__ dict __ ['foo'] = barとほぼ同じであるか、同様の呼び出しになります。

クラスメソッド、記述子、クラス変数、プロパティ、__ dict__オブジェクト内のクラスの静的メソッドはありません。これらはすべて__class__属性のクラスを使用して動的に定義され、オブジェクト自体ではなくオブジェクトのクラス(タイプ)に固有です。

例。 オブジェクトaのクラスを再定義します。

>>> class B ( object ):
... qux = 'B'
... def __init__ ( self ):
... self . name = 'B object'
... def bar ( self ):
... print 'bar'
...
>>> a . __dict__
{'name': 'a'}
>>> a . foo()
foo
>>> a . __class__
<class '__main__.A'>
>>> a . __class__ = B
>>> a . __class__
<class '__main__.B'>


変更点を確認します。

a.nameの値は同じままです。 クラスを変更するときに__init__が呼び出されませんでした。

>>> a . __dict__
{'name': 'a'}

「過去の」クラスAのクラス変数とメソッドへのアクセスが失われました。
>>> a . foo()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'B' object has no attribute 'foo'

そして、クラスBアクセスのクラス変数とメソッドは次のとおりです。
>>> a . bar()
bar
>>> a . qux
'B'


オブジェクト属性の操作:インストール、削除、および検索は、組み込み関数settattr、delattr、getattrの呼び出しと同等です。

ax = 1 <==> setattr(a、 'x'、1)
del ax <==> delattr(a、 'x')
ax <==> getattr(a、 'x')

setattrとdelattrはオブジェクト自体(より正確には、.__ dict__)のみに影響し、変更し、オブジェクトのクラスは変更しないことを理解する必要があります。

qux-クラス変数、つまり それはオブジェクトBではなくクラスBに「属しています」。

>>> a . qux
'B'
>>> a . __dict__
{'name': 'a'}


この属性を削除しようとすると、エラーが発生します。 delattrは、.__ dict__から属性を削除しようとします。

>>> delattr (a, 'qux' )
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: qux
>>> del a . qux
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: qux
>>> a . qux
'B'
>>>


さらに、属性を変更(設定)しようとすると、setattrはこの特定のオブジェクトに固有の__dict__にそれを配置します。

>>> b = B()
>>> b . qux
'B'
>>> a . qux = 'myB'
>>> a . qux
'myB'
>>> a . __dict__
{'qux': 'myB', 'name': 'a'}
>>> b . qux
'B'
>>>


まあ、__ dict__オブジェクトには 'qux'があるため、delattrを使用して削除できます。

>>> del a . qux

削除後、a.quxはクラス変数の値を返します。

>>> a . qux
'B'
>>> a . __dict__
{'name': 'a'}


だから:

オブジェクトとクラス


クラスはオブジェクトであり、特別な属性__class__と__dict__も持っています。

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


クラスには型typeがあります。

>>> A . __class__
<type 'type'>


真の__dict__クラスには辞書がありません

>>> A . __dict__
<dictproxy object at 0x1111e88>


ただし、__ dict__は、メソッド、記述子、変数、プロパティなどを格納する内部名前空間へのアクセスを担当します。

>>> dict (A . __dict__)
{'__module__': '__main__', 'qux': 'A', '__dict__': <attribute '__dict__' of 'A' objects>, 'foo': <function foo at 0x7f7797a25c08>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None}
>>> A . __dict__ . keys()
['__module__', 'qux', '__dict__', 'foo', '__weakref__', '__doc__']<


クラスには、__ class__および__dict__に加えて、いくつかの特別な属性があります。__bases__-直接の親のリスト、__ name__-クラスの名前。 [1]

クラスは、型インターフェースを実装する通常のオブジェクトのある種の拡張と考えることができます。 すべてのクラス(または型)のセットは、すべてのオブジェクトのセットに属します。つまり、そのサブセットです。 つまり、クラスはオブジェクトですが、すべてのオブジェクトがクラスではありません。 通常のオブジェクトをクラスではないオブジェクトと呼ぶことに同意します。

少し後でよく理解されるようになる小さなデモ。
クラスはオブジェクトです。
>>> class A ( object ):
... pass
...


>>> isinstance (A, object )
True


数字もオブジェクトです。

>>> isinstance ( 42 , object )
True


クラスはクラス(つまり、タイプ)です。

>>> isinstance (A, type )
True

ただし、この番号はクラス(タイプ)ではありません。 (タイプとは後で説明します)

>>> isinstance ( 42 , type )
False
>>>


まあ、aも普通のオブジェクトです。

>>> a = A()
>>> isinstance (a, A)
True
>>> isinstance (a, object )
True
>>> isinstance (a, type )
False


Aには、直接の親クラスが1つだけあります-オブジェクトです。

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


いくつかの特別なパラメーターも変更できます。

>>> A . __name__
'A'
>>> A . __name__ = 'B'
>>> A
<class '__main__.B'>


getattrを使用して、クラス属性にアクセスします。

>>> A . qux
'A'
>>> A . foo
<unbound method A.foo>
>>>


通常のオブジェクトの属性を検索する


最初の近似では、検索アルゴリズムは次のようになります:最初にオブジェクトの__dict__で検索され、次にオブジェクトのクラス(__class__を使用して決定される)の__dict__辞書と基本クラスの__dict__を再帰順に検索します。

例。

>>> class A ( object ):
... qux = 'A'
... def __init__ ( self , name):
... self . name = name
... def foo ( self ):
... print 'foo'
...
>>> a = A()
>>> b = A()


なぜなら 通常のオブジェクトaおよびbでは、__ dict__に 'qux'属性はありません。そのタイプ(クラス)の内部ディクショナリ__dict__で検索が続行され、__ dict__親ディクショナリに従って特定の順序で:

>>> b . qux
'A'
>>> A . qux
'A'


クラスAのqux属性を変更します。それに応じて、クラスA-aおよびbのインスタンスを返す値を変更する必要があります。

>>> A . qux = 'B'
>>> a . qux
'B'
>>> b . qux
'B'
>>>


同様に、実行時に、クラスにメソッドを追加できます。

>>> A . quux = lambda self : 'i have quux method'
>>> A . __dict__[ 'quux' ]
<function <lambda> at 0x7f7797a25b90>
>>> A . quux
<unbound method A.<lambda>>


そして、インスタンスへのアクセスが表示されます:

>>> a . quux()
'i have quux method'


他のオブジェクトと同様に、クラス属性(クラス変数quxなど)を削除できます。

>>> del A . qux

彼女は__dict__から引退します

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


また、インスタンスへのアクセスは失われます。

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


クラスは通常のオブジェクトとほとんど同じ属性検索を行いますが、違いがあります。検索は独自の__dict__辞書から始まり、特定のアルゴリズム、および__class__のクラスによってスーパークラスの__dict__辞書(__bases__に格納されています)を検索し、彼のスーパークラス。 (これについては後で詳しく説明します)。

リンク集




注釈


[1]簡単にするために、__ module__と__doc__を忘れましょう。 クラス属性の完全なリストはドキュメントにあります。

続きを読む


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

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


All Articles