views:

339

answers:

5

In python, is there a way to create a class that is treated like a dictionary but have the keys pre-defined when a new instance is created?

+5  A: 

You can easily extend any built in type. This is how you'd do it with a dict:

>>> class MyClass(dict):
...     def __init__(self, *args, **kwargs):
...             self['mykey'] = 'myvalue'
...             self['mykey2'] = 'myvalue2'
...
>>> x = MyClass()
>>> x['mykey']
'myvalue'
>>> x
{'mykey2': 'myvalue2', 'mykey': 'myvalue'}

I wasn't able to find the Python documentation that talks about this, but the very popular book Dive Into Python (available for free online) has a few examples on doing this.

Paolo Bergantino
Ah, thank you for satiating my curiosity.
Levi Campbell
I think you should add the call to dict.__init__ from http://stackoverflow.com/questions/817884/creating-dictionaries-with-pre-defined-keys/818019#818019 (or even better, user super).
Sverre Rabbelier
That's how I had it originally (check out the history), but after some research you apparently don't need it in this case. According to the Dive Into Python link above: "dict does not work like this; it is not a wrapper, and it requires no explicit initialization." - I'd love some more details on that, though, as it was my first instinct to have the super call in there.
Paolo Bergantino
It used to be the case that you couldn't subclass types using __init__ at all; you had to muck around with __new__. Perhaps it's related to that.
John Fouhy
A: 

Just create a subclass of dict and add the keys in the init method.

class MyClass(dict)

def __init__(self):
    """Creates a new dict with default values""""

    self['key1'] = 'value1'

Remember though, that in python any class that 'acts like a dict' is usually treated like one, so you don't have to worry too much about it being a subclass, you could instead implement the dict methods, although the above approach is probably more useful to you :).

Sverre Rabbelier
+3  A: 

Yes, in Python dict is a class , so you can subclass it:

    class SubDict(dict):
        def __init__(self):
            dict.__init__(self)
            self.update({
                'foo': 'bar',
                'baz': 'spam',})

Here you override dict's __init__() method (a method which is called when an instance of the class is created). Inside __init__ you first call supercalss's __init__() method, which is a common practice when you whant to expand on the functionality of the base class. Then you update the new instance of SubDictionary with your initial data.

    subDict = SubDict()
    print subDict    # prints {'foo': 'bar', 'baz': 'spam'}
Alex
+1  A: 

You can also have the dict subclass restrict the keys to a predefined list, by overriding __setitem__()

>>> class LimitedDict(dict):
    _keys = "a b c".split()
    def __init__(self, valtype=int):
     for key in LimitedDict._keys:
      self[key] = valtype()
    def __setitem__(self, key, val):
     if key not in LimitedDict._keys:
      raise KeyError
     dict.__setitem__(self, key, val)


>>> limited = LimitedDict()
>>> limited['a']
0
>>> limited['a'] = 3
>>> limited['a']
3
>>> limited['z'] = 0

Traceback (most recent call last):
  File "<pyshell#61>", line 1, in <module>
    limited['z'] = 0
  File "<pyshell#56>", line 8, in __setitem__
    raise KeyError
KeyError
>>> len(limited)
3
Ryan Ginstrom
A: 

I'm not sure this is what you're looking for, but when I read your post I immediately thought you were looking to dynamically generate keys for counting exercises.

Unlike perl, which will do this for you by default,


grep{$_{$_}++} qw/ a a b c c c /;
print map{$_."\t".$_{$_}."\n"} sort {$_{$b}$_{$a}} keys %_;

c   3
a   2
b   1

Python won't give you this for free:


l = ["a","a","b","c","c","c"]
d = {}
for item in l:
    d[item] += 1

Traceback (most recent call last):
  File "./y.py", line 6, in 
    d[item] += 1
KeyError: 'a'

however, defaultdict will do this for you,


from collections import defaultdict
from operator import itemgetter

l = ["a","a","b","c","c","c"]
d = defaultdict(int)
for item in l:
    d[item] += 1

dl = sorted(d.items(),key=itemgetter(1), reverse=True)
for item in dl:
    print item

('c', 3)
('a', 2)
('b', 1)
blackkettle
Not what I was wanting, but thanks for trying though.
Levi Campbell