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

Pythonオブジェクトシステムに関するメモの3番目の部分( 最初2番目の部分)。 この記事では、c .__ call __()がc()と同じではない理由、メタクラスを使用してシングルトンを実装する方法、マングリングの名前とその機能について説明します。







c .__ call__ vs c()、c .__ setattr__ vs setattr



x(arg1、arg2)が新しいクラスのx .__ call __(arg1、arg2)と同等ではないことを確認するのは簡単ですが、これは古いクラスには当てはまります。



>>> class C ( object ):

... pass

...

>>> c = C()

>>> c . __call__ = lambda : 42

>>> c()

Traceback (most recent call last):

File "<stdin>" , line 1 , in <module>

TypeError : 'C' object is not callable

>>> C . __call__ = lambda self : 42

>>> c()

42








実際に正しい:



c() <=> タイプ © __call __(s)



__setattr __ / setattrおよびその他の多くのマジック(および特別な)メソッドと、タイプ-クラスのオブジェクトを含むすべてのオブジェクトに対して定義されている対応する組み込み関数の状況はまったく同じです。



これが行われた理由は、setattr [1]の例で検討できます。

まずsetattr (a、 'x'1<==> type (a)を確認します。 __setattr __(a、 'x'1 )。



x = 1 <=> setattr (a、 'x'1



>>> class A ( object ): pass

...

>>> a = A()

>>> a . x = 1

>>> a

<__main__.A object at 0x7fafa9b26f90>

>>> setattr (a, 'y' , 2 )

>>> a . __dict__

{'y': 2, 'x': 1}








__setattr__メソッドを使用して、__ dict__に移動する新しい属性を設定します



>>> a . __setattr__( 'z' , 3 )







すべてが正しいようです:



>>> a . __dict__

{'y': 2, 'x': 1, 'z': 3}








ただし:



正しくないメソッドを.__ setattr__に設定します。



>>> a . __setattr__ = lambda self : 42







エラーを引き起こす呼び出し:



>>> a . __setattr__( 'z' , 4 )

Traceback (most recent call last):

File "<stdin>" , line 1 , in <module>

TypeError : <lambda>() takes exactly 1 argument (2 given)








ただし、これにもかかわらず、setattrは機能します。



>>> setattr (a, 'foo' , 'bar' )

>>> a . __dict__

{'y': 2, 'x': 1, '__setattr__': <function <lambda> at 0x7fafa9b3a140>, 'z': 3, 'foo': 'bar'}








ただし、クラスメソッドをオーバーライドする場合:



>>> A . __setattr__ = lambda self : 42







クラスインスタンスのsetattrはエラーをスローします。



>>> setattr (a, 'baz' , 'quux' )

Traceback (most recent call last):

File "<stdin>" , line 1 , in <module>

TypeError : <lambda>() takes exactly 1 argument (3 given)








なぜこれが行われたのですか?

setattr(a、 'x'、1)を.__ setattr __( 'x'、1)と同じにして、



>>> class A ( object ):

... def __setattr__ ( self , attr, value):

... print 'for instances' , attr, value

... object . __setattr__( self , attr, value)

...

>>> a = A()








aax = 1 <==> a .__ setattr __( 'x'、1)に新しい属性を設定します

すべて順調です:



>>> a . __setattr__( 'x' , 1 )

for instances x 1

>>> a . __dict__

{'x': 1}








次に、オブジェクトでもあるクラス自体に新しい属性を設定してみましょう。A.foo = 'bar' <==> A .__ setattr __( 'foo'、 'bar')



>>> A . __setattr__( 'foo' , 'bar' )

Traceback (most recent call last):

File "<stdin>" , line 1 , in <module>

TypeError : unbound method __setattr__() must be called with A instance as first argument (got str instance instead)








すべてが論理的であり、クラス(タイプ)の属性を検索するアルゴリズムに従って、最初にクラス(タイプ)の__dict__で属性が検索されます。



>>> A . __dict__[ '__setattr__' ]

<function __setattr__ at 0x7f699d22fa28>








ただし、実際には、クラス自体ではなく、クラスのインスタンスを対象としています。 したがって、A .__ setattr __( 'foo'、 'bar')の呼び出しは正しくありません。 そして、それがsetattr()がオブジェクトのクラス(タイプ)で明示的な検索を行わなければならない理由です。 実際、同じ理由で、これは他の魔法のメソッド__add __、__ len __、__ getattr__などに対して行われました。



呼び出し可能な型としてのクラス



クラス(タイプ)は呼び出し可能なタイプであり、その呼び出しはオブジェクトコンストラクターです。



>>> class C ( object ):

... pass

...

>>> ()

<__main__.C object at 0x1121e10>








と同等:



>>> type (C) . __call__(C)

<__main__.C object at 0x1121ed0>








なぜなら Cは通常のクラスであり、そのメタクラスはtypeであるため、type(C).__ call __(C)<==> type .__ call __(C)への呼び出しが使用されます。 タイプ.__ call __(C)内で、C .__ new __(cls、...)およびC .__ init __(self、...)はすでに呼び出されています。



重要なことは、__ new__と__init__の両方が、クラス内の属性を見つけるための通常のアルゴリズムを使用して検索されることです。 そして、C .__ dict__にそれらが存在しない場合、親クラスオブジェクトからのメソッド:object .__ new__およびobject .__ init__が呼び出されますが、__ call__メソッドはオブジェクトのクラス(タイプ)-タイプ:タイプ.__ call __(C)のメソッドです。



シングルトンv。2



これを知って、メタクラスのシングルトン実装を作成します。



シングルトンには何が必要ですか? A()を呼び出して同じオブジェクトを返します。



A()<=>タイプ(A).__ call __(A)



そのため、メタクラスで定義されている__call__メソッドの動作を変更する必要があります。 一般に、すべてのパラメーターを__call__に渡すことができることを忘れずにこれを行います。



>>> class SingletonMeta ( type ):

... def __call__ (cls, * args, ** kw):

... return super (SingletonMeta, cls) . __call__( * args, ** kw)

...

>>>








スタブの準備ができました。

単一のオブジェクトをクラス属性インスタンスに保存します。 これを行うには、__ init__のcls.instanceで初期化します。



>>> class SingletonMeta ( type ):

... def __init__ (cls, * args, ** kw):

... cls . instance = None

... def __call__ (cls, * args, ** kw):

... return super (SingletonMeta, cls) . __call__( * args, ** kw)

...

>>>





__call__:



>>> class SingletonMeta ( type ):

... def __init__ (cls, * args, ** kw):

... cls . instance = None

... def __call__ (cls, * args, ** kw):

... if cls . instance is None :

... cls . instance = super (SingletonMeta, cls) . __call__( * args, ** kw)

... return cls . instance

...

>>> class C ( object ):

... __metaclass__ = SingletonMeta

...








すべてが正常に機能することを確認します。



>>> C() is C()

True

>>> a = C()

>>> b = C()

>>> a . x = 42

>>> b . x

42

>>>








メタクラスとしての呼び出し可能な型



メタクラスは、型typeのオブジェクトだけでなく、一般的には呼び出し可能なすべての型にすることができます。



型メタクラスを使用してクラスが作成される関数を作成するだけです。



>>> def mymeta (name, bases, attrs):

... attrs[ 'foo' ] = 'bar'

... return type (name, bases, attrs)

...

>>> class D ( object ):

... __metaclass__ = mymeta

...

>>> D()

<__main__.D object at 0x7fafa9abc090>

>>> d = D()

>>> d . foo

'bar'

>>> d . __dict__

{}

>>> D . __dict__

<dictproxy object at 0x7fafa9b297f8>

>>> dict (D . __dict__)

{'__module__': '__main__', '__metaclass__': <function mymeta at 0x7fafa9b3a9b0>, '__dict__': <attribute '__dict__' of 'D' objects>, 'foo': 'bar', '__weakref__': <attribute '__weakref__' of 'D' objects>, '__doc__': None}








クラス定義



クラス定義のステートメントは単なる構造です。 他のステートメントと同様に、プログラムコードのどこにでも表示できます。



>>> if True :

... class A ( object ):

... def foo ( self ):

... print 42

...

>>> A

<class '__main__.A'>

>>> A() . foo()

42

>>>








「クラス」構造では、内部で定義された変数、関数、クラスが__dict__に蓄積されます。 そして、定義では、他の構造を使用できます-ループ、ifの:。



したがって、これを行うことができます。



>>> class A ( object ):

... if 1 > 2 :

... def foo ( self ):

... print '1>2'

... else :

... def bar ( self ):

... print 'else'

...

>>>

>>> A()

<__main__.A object at 0x7fafa9abc150>

>>> A() . foo()

Traceback (most recent call last):

File "<stdin>" , line 1 , in <module>

AttributeError : 'A' object has no attribute 'foo'

>>> A() . bar()

else








かそこら

>>> class A ( object ):

... if 1 > 2 :

... x = 1

... def foo ( self ):

... print 'if'

... else :

... y = 1

... def bar ( self ):

... print 'else'

...

>>> A . x

Traceback (most recent call last):

File "<stdin>" , line 1 , in <module>

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

>>> A . y

1

>>> A . foo

Traceback (most recent call last):

File "<stdin>" , line 1 , in <module>

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

>>> A . bar

<unbound method A.bar>

>>> A . bar()

Traceback (most recent call last):

File "<stdin>" , line 1 , in <module>

TypeError : unbound method bar() must be called with A instance as first argument (got nothing instead)

>>> A() . bar()

else

>>>








ある定義を別の定義にネストできます。



>>> class A ( object ):

... class B ( object ):

... pass

...

...

>>> A()

<__main__.A object at 0x7fafa9abc2d0>

>>> A . __dict__

<dictproxy object at 0x7fafa9b340f8>

>>> dict (A . __dict__)

{'__dict__': <attribute '__dict__' of 'A' objects>, '__module__': '__main__', 'B': <class '__main__.B'>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None}

>>> A . B()

<__main__.B object at 0x7fafa9abc310>









または、クラスメソッドを動的に作成します。



>>> FIELDS = [ 'a' , 'b' , 'c' ]

>>> class A ( object ):

... for f in FIELDS:

... locals ()[f] = lambda self : 42

...

>>> a = A()

>>> a . a()

42

>>> a . b()

42

>>> a . c()

42

>>> a . d()

Traceback (most recent call last):

File "<stdin>" , line 1 , in <module>

AttributeError : 'A' object has no attribute 'd'

>>>








もちろん、これは確かに通常の実践では行わないことを強くお勧めしますが、より慣用的な手段を使用することをお勧めします。



名前のマングリング



クラス定義について。 名前のマングリングについて。



「.__ {attr}」という形式のクラスのクラス名定義内の属性(同時に、attrは最後に1つしか_を持たない)は、「_ {classname} __ {attr}」に置き換えられます。 したがって、クラス内では、クラスの継承者とインスタンスに「見えない」「隠された」プライベート属性を持つことができます。



>>> class A ( object ):

... __private_foo =1

...

>>> A . __private_foo

Traceback (most recent call last):

File "<stdin>" , line 1 , in <module>

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








このような変数を見ることができます:



>>> A . _A__private_foo

1








まあ、それは__dict__クラスに保存されています:



>>> dict (A . __dict__)

{'__dict__': <attribute '__dict__' of 'A' objects>, '_A__private_foo': 1, '__module__': '__main__', '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None}

>>>








後継者はアクセスできません。



>>> class B (A):

... def foo ( self ):

... print self . __private_foo

...

>>> B() . foo()

Traceback (most recent call last):

File "<stdin>" , line 1 , in <module>

File "<stdin>" , line 3 , in foo

AttributeError : 'B' object has no attribute '_B__private_foo'








原則として、クラス定義内のタイプ__ {attr}の属性への外部アクセスへのアクセスを提供します。 __dict__でname_manglingを回避できます。



>>> class C ( object ):

... def __init__ ( self ):

... self . __dict__[ '__value' ] = 1

...

>>> C() . __value

1

>>>








ただし、 ".__ {attr}"が "._ {classname} __ {attr}"に置き換えられるため、このような属性へのアクセスは他のクラスの定義内では不可能になるため、このようなことは強く推奨されません。どのオブジェクトまたはクラスに属しているか、つまり



>>> class D ( object ):

... def __init__ ( self ):

... self . c = C() . __value

...

>>> D()

Traceback (most recent call last):

File "<stdin>" , line 1 , in <module>

File "<stdin>" , line 3 , in __init__

AttributeError : 'C' object has no attribute '_D__value'

>>> C() . __value

1

>>>








C().__の値は、クラス定義の外では問題なく機能しますが。 回避するには、__ dict __ ['__ value']も使用する必要があります。



リンク集

注釈



[1]公式ドキュメントでは __ len __ / lenおよび__hash __ / hashのを提供しています。



All Articles