0

I have a class that inherit from OrderedDict, but I don't know if this is the right way to accomplish what I need.

I would like the class to have the duel method of the javascript '.' notation like obj.<property> and I would also like the users to be able to access the class properties like obj['myproperty'] but I was to hide all the key() and get() functions. The inheritance model is providing good functionality, but it cluttering up the object with additional methods that are not really needed.

Is it possible to get the dictionary behavior without all the other functions coming along?

For this discussion, let's assume my class is this:

from six.moves.urllib import request
import json
class MyClass(OrderedDict):
     def __init__(self, url):
        super(MyClass, self).__init__(url=url)
        self._url = url
        self.init()
     def init(self):
        # call the url and load the json
        req = request.Request(self._url)
        res = json.loads(request.urlopen(req).read())
        for k,v in res.items():
           setattr(self, k, v)
        self.update(res)
        self.__dict__.update(res)

if __name__ == "__main__":
    url = "https://sampleserver5.arcgisonline.com/ArcGIS/rest/services?f=json"
    props = MyClass(url=url)
    props.currentVersion

Is there another way to approach this dilemma?

Thanks

code base 5000
  • 3,812
  • 13
  • 44
  • 73
  • @LutzHorn I saw this post, and I felt it was different enough. – code base 5000 May 31 '16 at 13:59
  • @LutzHorn - I know that, but I feel this is more about should dictionary be used for inheritance in mimicking the bracket method and JS dot notation than in 'private'. I think you are too quick to bounce :) – code base 5000 May 31 '16 at 14:01
  • 1
    Then maybe don't use inheritance but a facade. –  May 31 '16 at 14:02
  • Can you remove the duplication flag so other can provide answers? Duplication is like the kiss of death here. – code base 5000 May 31 '16 at 14:03
  • Sorry, but I can't remove the flag. Answers are of course possible since the question is not closed or on hold. –  May 31 '16 at 14:04
  • @LutzHorn - can you please provide an example of your facade suggestion? – code base 5000 May 31 '16 at 14:12
  • @LutzHorn you can delete the comment if you no longer think it applies, but don't worry josh as your question does not have any close _votes_ so you are not at risk for being incorrectly marked a duplicate. – Tadhg McDonald-Jensen May 31 '16 at 14:13
  • Why do you have a method called `init`? It looks completely pointless and I thought you wanted to avoid extra methods. Similarly is `_url` needed by any users of the class, or can you just discard it? – Alex Hall May 31 '16 at 14:19
  • 1
    @josh1234 why do you want to use OrderedDict if you don't want to use `.keys()` or `.items()` or other methods that accompany dictionaries? you may consider looking at [Accessing dict keys like an attribute in Python?](http://stackoverflow.com/questions/4984647/accessing-dict-keys-like-an-attribute-in-python) if you have not already but if you only need lookups I'd think it would be more beneficial to forward dictionary lookups to attribute lookups so that you don't need to inherit from `dict` at all. – Tadhg McDonald-Jensen May 31 '16 at 14:20

1 Answers1

3

If all you want is x['a'] to work the same way as x.a without any other functionality of dictionaries, then don't inherit from dict or OrderedDict, instead just forward key/indice operations (__getitem__, __setitem__ and __delitem__) to attribute operations:

class MyClass(object):
    def __getitem__(self,key):
        try: #change the error to a KeyError if the attribute doesn't exist
            return getattr(self,key)
        except AttributeError:
            pass
        raise KeyError(key)

    def __setitem__(self,key,value):
        setattr(self,key,value)
    def __delitem__(self,key):
        delattr(self,key)

As an added bonus, because these special methods don't check the instance variables for the method name it doesn't break if you use the same names:

x = MyClass()
x['__getitem__'] = 1
print(x.__getitem__) #still works
print(x["__getattr__"]) #still works

The only time it will break is when trying to use __dict__ since that is where the instance variables are actually stored:

>>> x = MyClass()
>>> x.a = 4
>>> x.__dict__ = 1 #stops you right away
Traceback (most recent call last):
  File "<pyshell#36>", line 1, in <module>
    x.__dict__ = 1
TypeError: __dict__ must be set to a dictionary, not a 'int'
>>> x.__dict__ = {} #this is legal but removes all the previously stored values!
>>> x.a
Traceback (most recent call last):
  File "<pyshell#38>", line 1, in <module>
    x.a
AttributeError: 'MyClass' object has no attribute 'a'

In addition you can still use the normal dictionary methods by using vars():

x = MyClass()
x.a = 4
x['b'] = 6
for k,v in vars(x).items():
    print((k,v))

#output
('b', 6)
('a', 4)
>>> vars(x)
{'b': 6, 'a': 4}
Tadhg McDonald-Jensen
  • 20,699
  • 5
  • 35
  • 59