4

So I have this (of course the true thing is more complicated) :

class test(object):
    i = -1

    def keys(self):
        return ["a", "b"]

    def __getitem__(self, item):
        return {"a": 0, "b": 1}[item]

    def __len__(self):
        return 2

    def __contains__(self, item):
        if item in ["a", "b"]:
            return True
        return False

    def __iter__(self):
        return self

    def next(self):
        self.i += 1
        if self.i > 1:
            raise StopIteration()

        return ["a", "b"][self.i]

What I want to do is this (again the true thing is more complex):

> t = test()
> dict(t)
 {'a': 0, 'b': 1}
> dict(**t) 
 {'a': 0, 'b': 1}

This work perfectly, but fail to work if I define the class as a subclass of a dict, and this is what I want, I want my object to behave like a dict with some hidden tricks under knees (and again sure it makes more sense in the real code):

class test(dict):
     .... same code here ....

In this case the dict(t) and dict(**t) will return an empty dictionary {}, but [k for k in t] will return ['a','b'].

What do I miss ? It seems that I do need to redeclare some dict function, I though that __getitem__, __iter__, __len__, __contains__ and keys method was enough to do the trick. I tried to redeclare iterkeys, itervalues, copy, get, etc, but nothing seems to work.

Thanks.

Bach
  • 6,145
  • 7
  • 36
  • 61
user3240484
  • 209
  • 1
  • 4
  • 1
    I think you need to redefine the constructor to call the super. – njzk2 Mar 20 '14 at 15:26
  • not if you aren't overriding `__init__` in the first place – wim Mar 20 '14 at 15:27
  • The `collections.UserDict` eases the creation of custom classes that behave like a dictionary. – Cilyan Mar 20 '14 at 15:28
  • Search for the python mapping protocol. You might look into inheriting `collections.abc.MutableMapping` instead of inheriting dict – wim Mar 20 '14 at 15:30
  • all the method you have shown so far are handled by the dict. by inheriting dict, all you need to do is call a super with your `{"a": 0, "b": 1}` as parameter – njzk2 Mar 20 '14 at 15:30
  • possible duplicate of [How to correctly implement the mapping protocol in Python?](http://stackoverflow.com/questions/19775685/how-to-correctly-implement-the-mapping-protocol-in-python) – wim Mar 20 '14 at 15:33
  • Thanks for all your comments. The best solution if found is to inherit from the class `UserDict.UserDict`, with one draw back that the `issubclass(test, dict)` does not work. I'll try to fix that with `__subclasshook__` – user3240484 Mar 20 '14 at 16:07

1 Answers1

0
 class test(dict):
    i = -1

    def __init__(self):
        super(test, self).__init__({"a": 0, "b": 1})

    def keys(self):
        return ["a", "b"]

    def __getitem__(self, item):
        return {"a": 0, "b": 1}[item]

    def __len__(self):
        return 2

    def __contains__(self, item):
        if item in ["a", "b"]:
            return True
        return False

    def __iter__(self):
        return self

    def next(self):
        self.i += 1
        if self.i > 1:
            raise StopIteration()

        return ["a", "b"][self.i]

t = test()
print dict(t)
# output: {'a': 0, 'b': 1}
print dict(**t)
# output: {'a': 0, 'b': 1}

Of course, hardcode {'a': 0, 'b': 1} in the class definition is not a good idea.

Cedar
  • 52
  • 5