2017-07-26 15 views
1

クラスAのすべてのインスタンスの__dict__がグローバルdict sharedを指すこの例を考えてみましょう。予想通りクラスで__dict__属性をオーバーライドした後にインスタンス辞書にアクセスするには?

>>> a = A() 
>>> b = A() 
>>> a.a, a.b, b.a, b.b 
(1, 2, 1, 2) 
>>> b.x = 100 
>>> shared 
{'a': 1, 'x': 100, 'b': 2} 
>>> a.x 
100 
>>> c = A() 
>>> c.a, c.b, c.x 
(1, 2, 100) 
>>> shared['foo'] = 'bar' 
>>> a.foo, b.foo, c.foo 
('bar', 'bar', 'bar') 
>>> a.__dict__, b.__dict__, c.__dict__ 
({'a': 1, 'x': 100, 'b': 2, 'foo': 'bar'}, 
{'a': 1, 'x': 100, 'b': 2, 'foo': 'bar'}, 
{'a': 1, 'x': 100, 'b': 2, 'foo': 'bar'} 
) 

すべての作品:

shared = {'a': 1, 'b': 2} 

class A(object): 
    def __init__(self): 
     self.__dict__ = shared 

今度は、いくつかのことをテストしてみましょう。


今度は__dict__という名前の属性を追加することにより、クラスA少し微調整しましょう。

shared = {'a': 1, 'b': 2} 

class A(object): 
    __dict__ = None 
    def __init__(self): 
     self.__dict__ = shared 

のは、再びステップの同じセットを実行してみましょう:上記の情報に基づいて

>>> a = A() 
>>> b = A() 
>>> a.a, a.b, b.a, b.b 
AttributeError: 'A' object has no attribute 'a' 
>>> b.x = 100 
>>> shared 
{'a': 1, 'b': 2} 
>>> b.__dict__ # What happened to x? 
{'a': 1, 'b': 2} 
>>> a.x 
AttributeError: 'A' object has no attribute 'x' 
>>> c = A() 
>>> c.a, c.b, c.x 
AttributeError: 'A' object has no attribute 'a' 
>>> shared['foo'] = 'bar' 
>>> a.foo, b.foo, c.foo 
AttributeError: 'A' object has no attribute 'foo' 
>>> a.__dict__, b.__dict__, c.__dict__ 
({'a': 1, 'b': 2, 'foo': 'bar'}, 
{'a': 1, 'b': 2, 'foo': 'bar'}, 
{'a': 1, 'b': 2, 'foo': 'bar'} 
) 
>>> b.x # Where did this come from? 
100 

予想通り最初のケースが働いていたが、二番目はしなかったので、私はしたいと思います追加クラスレベル__dict__の属性の後で何が変更されたかを知る。今使用されているインスタンス辞書にはどのような方法でもアクセスできますか?

+0

import ctypes def magic_get_dict(o): # find address of dict whose offset is stored in the type dict_addr = id(o) + type(o).__dictoffset__ # retrieve the dict object itself dict_ptr = ctypes.cast(dict_addr, ctypes.POINTER(ctypes.py_object)) return dict_ptr.contents.value 

第二ケースを継続します。 [**予約済みクラスの識別子**](https://docs.python.org/3/reference/lexical_analysis.html#reserved-classes-of-identifiers)を参照してください。 – martineau

+0

@martineau私はそれを認識しています。しかし、それはこの問題のポイントではありません。 –

+0

ガイドラインに従わないために壊れていたものの回避策を知りたがっています。 – martineau

答えて

3

最初のケースでは、self.__dict__は、そのタイプによって提供される__dict__記述子にアクセスできます。この記述子を使用すると、基底のインスタンス辞書を取得し、PyObject_GenericGetDictおよびPyObject_GenericSetDictをそれぞれ使用して新しいインスタンス辞書を設定することができます。

>>> A.__dict__ 
mappingproxy(
{'__module__': '__main__', 
'__init__': <function A.__init__ at 0x1041fb598>, 
'__dict__': <attribute '__dict__' of 'A' objects>, 
'__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None 
}) 
>>> A.__dict__['__dict__'].__get__(a) 
{'a': 1, 'b': 2} 

そしてもちろん、我々としても、ここから新しい辞書を設定することができます。

>>> new_dict = {} 
>>> A.__dict__['__dict__'].__set__(a, new_dict) # a.__dict__ = new_dict 
>>> a.spam = 'eggs' 
>>> a.__dict__ 
{'spam': 'eggs'} 
>>> new_dict 
{'spam': 'eggs'} 
>>> b = A() # Points to `shared` 
>>> b.__dict__ 
{'a': 1, 'b': 2} 

後者の場合には、当社のクラス自体は__dict__という名前の属性が含まれていますが、それでも__dict__属性ポイント〜mappingproxy。このようにクラスのための

>>> A.__dict__ 
mappingproxy(
{'__module__': '__main__', 
'__dict__': None, 
'__init__': <function A.__init__ at 0x1041cfae8>, 
'__weakref__': <attribute '__weakref__' of 'A' objects>, 
'__doc__': None} 
) 

__dict__属性がspecial attributeです。

>>> A.__weakref__ is A.__dict__['__weakref__'] 
True  
>>> A.__weakref__ = 1  
>>> A.__weakref__, A.__dict__['__weakref__'] 
(1, 1) 

>>> A.__dict__ = {}  
AttributeError: attribute '__dict__' of 'type' objects is not writable 

我々が設定していた属性は、このようにアクセスすることができます。

>>> repr(A.__dict__['__dict__']) 
'None' 

A Pythonのレベル、我々は今、インスタンス辞書へのアクセスを失っているが、内部クラスはtp_dictoffsetを使用してそれを見つけることができます。 _PyObject_GetDictPtrで行われます。

__getattribute____setattr__も、_PyObject_GetDictPtrを使用して、基になるインスタンス辞書にアクセスします。私たちは実際のctypesを使ってPythonで_PyObject_GetDictPtrを実装することができます使用されているインスタンス辞書にアクセスするには

。これは@ user4815162342 hereによってかなり雄弁に行われます。あなたがリードすると、システム定義の名前のために予約されている2つの下線文字を末尾に持つ属性を追加すべきではない

>>> magic_get_dict(a) 
{'__dict__': {'a': 1, 'b': 2, 'foo': 'bar'}} # `a` has only one attribute i.e. __dict__ 
>>> magic_get_dict(b) 
{'__dict__': {'a': 1, 'b': 2, 'foo': 'bar'}, 'x': 100} # `x` found 
>>> magic_get_dict(b).update(shared) 
>>> b.a, b.b, b.foo, b.x 
(1, 2, 'bar', 100) 
+1

また、 '_PyObject_GetDictPtr'に' ctypes.pythonapi._PyObject_GetDictPtr'で直接アクセスすることもできます。しかし、あなたはargtypesとrestypeを手動で設定する必要があります。 – user2357112

関連する問題