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)

      

+1


source to share


5 answers


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's setattr(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.

+3


source


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'

      

+4


source


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))

      

+2


source


You can directly update the attribute of __dict__

your object, which I assume is:

class MyClass:

    def __init__(self):
        self.mydict = {'var1': 'value1', 'var2': 'value2', ...}
        self.__dict__.update(self.mydict)

      

-1


source


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).

-1


source







All Articles