python - How to access instance dictionary after overriding __dict__ attribute on its class? -
consider example __dict__
of instances of class a
point global dict shared
.
shared = {'a': 1, 'b': 2} class a(object): def __init__(self): self.__dict__ = shared
now let's test few things:
>>> = 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'} )
all works expected.
now let's tweak class a
little adding attribute named __dict__
.
shared = {'a': 1, 'b': 2} class a(object): __dict__ = none def __init__(self): self.__dict__ = shared
let's run same set of steps again:
>>> = 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__ # happened 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 # did come from? 100
based on above information first case worked expected second 1 didn't , hence know changed after adding class level __dict__
attribute. , can access instance dictionary being used in way?
in first case self.__dict__
has access __dict__
descriptor provided type. descriptor allows underlying instance dictionary , set new 1 using pyobject_genericgetdict
, pyobject_genericsetdict
respectively.
>>> a.__dict__ mappingproxy( {'__module__': '__main__', '__init__': <function a.__init__ @ 0x1041fb598>, '__dict__': <attribute '__dict__' of 'a' objects>, '__weakref__': <attribute '__weakref__' of 'a' objects>, '__doc__': none }) >>> a.__dict__['__dict__'].__get__(a) {'a': 1, 'b': 2}
and of course can set new dictionary here well:
>>> 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 `shared` >>> b.__dict__ {'a': 1, 'b': 2}
in second case our class contains attribute named __dict__
, still __dict__
attribute points mappingproxy
.
>>> a.__dict__ mappingproxy( {'__module__': '__main__', '__dict__': none, '__init__': <function a.__init__ @ 0x1041cfae8>, '__weakref__': <attribute '__weakref__' of 'a' objects>, '__doc__': none} )
__dict__
attribute classes in way special attribute.
>>> a.__weakref__ a.__dict__['__weakref__'] true >>> a.__weakref__ = 1 >>> a.__weakref__, a.__dict__['__weakref__'] (1, 1) >>> a.__dict__ = {} attributeerror: attribute '__dict__' of 'type' objects not writable
the attribute had set can accessed this:
>>> repr(a.__dict__['__dict__']) 'none'
a python level have lost access instance dictionary internally class can find using tp_dictoffset
. done in _pyobject_getdictptr
.
both __getattribute__
, __setattr__
access underlying instance dictionary using _pyobject_getdictptr
.
to access instance dictionary being used can implement _pyobject_getdictptr
in python using ctypes. pretty eloquently done @user4815162342 here.
import ctypes def magic_get_dict(o): # find address of dict offset stored in type dict_addr = id(o) + type(o).__dictoffset__ # retrieve dict object dict_ptr = ctypes.cast(dict_addr, ctypes.pointer(ctypes.py_object)) return dict_ptr.contents.value
continuing second case:
>>> magic_get_dict(a) {'__dict__': {'a': 1, 'b': 2, 'foo': 'bar'}} # `a` has 1 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)
Comments
Post a Comment