Converting dict attributes to multiple attributes in Python
I have a class with a dict attribute, for example:
class MyClass:
def __init__(self):
self.mydict = {'var1': 'value1', 'var2': 'value2', ...}
When I want to get the values, I have to do this:
cls = MyClass()
print(cls.mydict['var1'])
print(cls.mydict['var2'])
What is the solution to get the values ββdirectly in the attributes please:
cls = MyClass()
print(cls.var1)
print(cls.var2)
source to share
You can add an extra function to the class that can parse the dict and insert the appropriate attributes:
def assign_variables( self ):
for key, val in self.mydict.items():
setattr( self, key, val )
I'm using the built-in setattr()
function here to set attributes with dynamic names / values:
This is an analogue
getattr()
. The arguments are an object, a string, and an arbitrary value. The string can name an existing attribute or a new attribute. The function assigns a value to an attribute if the object allows it.
For example, it'ssetattr(x, 'foobar', 123)
equivalentx.foobar = 123
.
You can call this function inside your constructor after the variable has been defined, mydict
or even just put the loop in the constructor.
source to share
Using setattr
, you can set the attribute dynamically:
>>> class MyClass:
... def __init__(self):
... mydict = {'var1': 'value1', 'var2': 'value2'}
... for key, value in mydict.items():
... setattr(self, key, value)
...
>>> instance = MyClass()
>>> instance.var1
'value1'
>>> instance.var2
'value2'
source to share
Another solution is to implement __getattr__
:
class MyClass(object):
def __init__(self):
self.mydict = {'var1': 'value1', 'var2': 'value2', ...}
def __getattr__(self, name):
try:
return self.mydict[name]
except KeyError:
raise AttributeError("object %s has no attribute '%s'" % (self, name))
source to share
Another way would be to override __getattr__
and __setattr__
together, which avoids having two object references for the same attribute in the class instance object (one OR on the value1
inside myobj.mydict['var1']
) and the other OR on value1
in myobj.__dict__['var1']
):
class MyClass():
def __init__(self):
self.__dict__['_mydict'] = {'var1': 'value1', 'var2': 'value2'}
#Note: using the @property (descriptor) is optional. I think it makes
#things more convenient.
@property
def mydict(self):
return self._mydict
#NOTE: __getattr__ won't get called if att is found in self.__dict__ first!
def __getattr__(self,att):
if att in self.mydict: # or without using @property: if att in self._mydict:
return self.mydict[att] # return self._mydict[att]
else:
raise AttributeError("'{n}' object has no attribute '{a}'".format(n = type(self).__name__, a = att))
def __setattr__(self,att,val):
super().__setattr__(att, val)
if att in self.mydict:
self.mydict[att] = val # or without using @property: self._mydict[att] = val
self.__delattr__(att) # remove duplicate copy of object reference to att
Note that doing this means that you cannot add additional key, value pairs in mydict
unless you call this property directly, for example:
myobj = MyClass()
myobj.x = 1
myobj.mydict #note that result does not contain an entry for 'x'
myobj.mydict['x'] = 2
myobj.mydict #now it does
Also note that getting and removing members mydict
will be overridden by any existing attribute with the same name (not only that, but you cannot remove members mydict
at all unless you also override __delattr__
to enable this behavior):
#continuing the above code session
assert myobj.x == 1 #myobj.x is coming from self.x, not from self._mydict['x']
myobj.var1 = 'a new value'
myobj.var1 #results in 'a new value' as expected, but where is it coming from?
assert myobj.mydict['var1'] == 'a new value' #Ah: it coming from self._mydict as expected
assert myobj.__dict__['var1'] #ERROR, as expected
del myobj.x
assert myobj.x == 2 #now myobj.x is coming from self._mydict['x'], self.x having been deleted
del myobj.var1 #raises AttributeError; attributes in mydict can't be deleted (unless you also override __delattr__
If you want to change this behavior, you must override __getattribute__
(EDIT: this, as bruno desthuilliers notes below, is usually not a good idea).
source to share