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

Popular posts from this blog

node.js - Node js - Trying to send POST request, but it is not loading javascript content -

javascript - Replicate keyboard event with html button -

javascript - Web audio api 5.1 surround example not working in firefox -