5

I am using a technique discussed here before, to turn a dictionary into an object, so that I can access the elements of the dictionary with the dot (.) notion, as instance variables.

This is what I am doing:

# Initial dictionary
myData = {'apple':'1', 'banana':'2', 'house':'3', 'car':'4', 'hippopotamus':'5'}

# Create the container class
class Struct:
    def __init__(self, **entries):
        self.__dict__.update(entries)

# Finally create the instance and bind the dictionary to it
k = Struct(**myData)

So now, I can do:

print k.apple

and the result is:

1

This works, however the issues start if I try to add some other methods to the "Struct" class. For example lets say that I am adding a simple method that just creates an variable:

class Struct:
    def __init__(self, **entries):
        self.__dict__.update(entries)

    def testMe(self):
        self.myVariable = 67

If I do:

k.testMe()

My dictionary object is broken, "myVariable" is inserted as a key with the value "67". So If I do:

print k.__dict__

I am getting:

{'apple': '1', 'house': '3', 'myVariable': 67, 'car': '4', 'banana': '2', 'hippopotamus': '5'}

Is there a way to fix this? I kind of understand what is happening, but not sure If I need to entirely change my approach and build a class with internal methods to handle the dictionary object or is there a simpler way to fix this problem?

Here is the original link: Convert Python dict to object?

Thanks.

Community
  • 1
  • 1
mbilyanov
  • 2,315
  • 4
  • 29
  • 49
  • Attributes of object instances are stored in the so called *instance dict* that can be accessed through `obj.__dict__` - did you intentionally choose that name for your dict because you want it to "look like a real object", or was that accidental? Because if not, you should just use something like `self._dict` (initialize it first) and you'll be good. – Lukas Graf Sep 30 '14 at 17:47
  • 1
    Or to phrase it differently: Yes, of course `myVariable` is inserted as a key to your object's `__dict__` - that's how it's supposed to work. Isn't that what you want? I don't see how this "breaks" your dict. – Lukas Graf Sep 30 '14 at 17:51
  • @LukasGraf Hi, thanks for the prompt reply. I have just followed the instructions in that link. Let me try, but I suspect that, If I do not use `__dict__` I am not going to get the `.item` access. I will try it now. – mbilyanov Sep 30 '14 at 17:51
  • No, you won't get dotted attribute access if you don't use `obj.__dict__`. You could however use a `namedtuple` (IIRC there's also an example for that in the other answer). – Lukas Graf Sep 30 '14 at 17:51
  • Well, I do not want every variable that I create in my class, to be inserted in the dictionary, I would like to be able to have some more internal functionality without contaminating my dictionary. If possible of course? – mbilyanov Sep 30 '14 at 17:52
  • so you want *two kinds* of attribute, which are the same for the purposes of `getattr()` but different in-re to `setattr()`? what's the actual problem you're trying to solve? – SingleNegationElimination Sep 30 '14 at 17:54
  • I am tying to create a dynamic object, that stores the dictionary keys and items, and gives access to them through the usual `obj.key = value` so that when I do: `s = Struct(**myDictionary)` then I can do `s.apple` and get the `value`. But also have additional methods inside that class that I can use for other things, for example: `s.formattedDump()` to print the dictionary in a formatted way etc. The problem with the current state is that I cannot really add any other method to that class, any methods I build will have internal variables, any attempt contaminates my dictionary. – mbilyanov Sep 30 '14 at 18:02

1 Answers1

3

For your needs, don't store you variables in __dict__. Use your own dictionary instead, and override .__getattr__ (for print k.apple) and __setattr__ (for k.apple=2):

# Initial dictionary
myData = {'apple':'1', 'banana':'2', 'house':'3', 'car':'4', 'hippopotamus':'5'}

# Create the container class
class Struct:
    _dict = {}
    def __init__(self, **entries):
        self._dict = entries

    def __getattr__(self, name):
        try:
            return self._dict[name]
        except KeyError:
            raise AttributeError(
                "'{}' object has no attribute or key '{}'".format(
                    self.__class__.__name__, name))


    def __setattr__(self, name, value):
        if name in self._dict:
            self._dict[name] = value
        else:
            self.__dict__[name] = value

    def testMe(self):
        self.myVariable = 67

    def FormattedDump(self):
        return str(self._dict)

# Finally create the instance and bind the dictionary to it
k = Struct(**myData)

print k.apple
print k.FormattedDump()
k.testMe()
k.apple = '2'
print k.FormattedDump()

In the alternative, if your FormattedDump() routine is bothering you, you could just fix it:

# Initial dictionary
myData = {'apple':'1', 'banana':'2', 'house':'3', 'car':'4', 'hippopotamus':'5'}

# Create the container class
class Struct:
    def __init__(self, **entries):
        self.__dict__.update(entries)
        self.public_names = entries.keys()

    def testMe(self):
        self.myVariable = 67

    def GetPublicDict(self):
        return {key:getattr(self, key) for key in self.public_names}
    def FormattedDump(self):
        return str(self.GetPublicDict())

# Finally create the instance and bind the dictionary to it
k = Struct(**myData)

print k.apple
print k.FormattedDump()
k.testMe()
k.apple = '2'
print k.FormattedDump()
Robᵩ
  • 163,533
  • 20
  • 239
  • 308
  • Removed my answer in favor of yours since you're gonna expand on `__setattr__`. – Lukas Graf Sep 30 '14 at 18:14
  • 1
    You should however raise `AttributeError` in `__getattr__`, a missing key in `self._dict` would otherwise raise a `KeyError` instead, which would be rather unexpected for dotted attribute access. – Lukas Graf Sep 30 '14 at 18:16
  • Hi! Thank you very much for the help! I will try it immediately. This already looks pretty cool, thanks for all the help and the advice. – mbilyanov Sep 30 '14 at 18:26
  • @symbolix - You are welcome. But do consider my 2nd alternative. If seems much more elegant to me. – Robᵩ Sep 30 '14 at 18:27
  • Sure. This looks like a valuable way to learn more about classes etc. I got really confused in my initial code, when any variable I was trying to utilise in order to perform an operation was being added to my dictionary. So the solution that was my starting point was actually wrong?! – mbilyanov Sep 30 '14 at 18:31
  • @symbolix the issue was that `__dict__` has a very special meaning in Python (like all `__magic__` names), and is used by Python itself. See [`obj.__dict__`](https://docs.python.org/2/library/stdtypes.html#object.__dict__) and [The Standard Type Hierarchy, Section *Class Instances*](https://docs.python.org/2/reference/datamodel.html#the-standard-type-hierarchy) – Lukas Graf Sep 30 '14 at 18:34
  • Works great! Thanks for the solution. Using the second, elegant approach :) – mbilyanov Sep 30 '14 at 21:01