I have a class with a dict attribute, like this :
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 attributes please :
cls = MyClass()
print(cls.var1)
print(cls.var2)
You could add an additional function to the class that will be able to parse the dict and insert the relevant 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 the counterpart of getattr()
. The arguments are an object, a string and an arbitrary value. The string may name an existing attribute or a new attribute. The function assigns the value to the attribute, provided the object allows it.
For example, setattr(x, 'foobar', 123)
is equivalent to x.foobar = 123
.
You can call this function inside your constructor after the mydict
variable is defined or even just place the loop in the constructor.
Using setattr
, you can set 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'
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))
You can directly update the __dict__
attribute of your object I believe:
class MyClass:
def __init__(self):
self.mydict = {'var1': 'value1', 'var2': 'value2', ...}
self.__dict__.update(self.mydict)
Another way would be to override __getattr__
and __setattr__
together, which avoids having two object references to the same attribute in the class instance object (one O.R. to value1
inside myobj.mydict['var1']
, and another O.R. to 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 it this way means you cannot add more key,value pairs to mydict
unless you call the property directly, e.g.:
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 deleting the mydict
members will be overridden by any existing attribute of the same name (not only that, but you can't delete mydict
members 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's 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 have to override __getattribute__
(EDIT: which, as bruno desthuilliers notes below, is usually not a good idea).