Python's dict() user interface pretty much blows; you end strewing quotes everywhere and having to remember the idiom for setting the first use of a key. On the other hand, lua's table (hash) user interface rocks. I want my python dict()s to look more like lua tables:

my_dict = Dict() my_dict.foo += ".py" my_dict.bar += 1

In python, we can use a descriptor type class to implement the basic usage; however the autovivification of an attribute and increment requires a little trickiness. We need to create a new class to override the typical operators. Ideally (?), python would have an IdentityType type that acts like an identity operator for types: i.e. IdentityType operating on any other type returns the other type.

Originally, I thought that NoneType or NotImplementedType might satisfy this; they don't. So, we have to implement a new type, IdentityType, and use it to deal with the autovivification.

Update: 2008-10-28
I cleaned up the Dict class by subclassing dict() and converted the unknown attribute operations to just call the __*item__ methods. And added a bunch more tests.

import types class UnknownType(object): """ provide type that becomes the type of the parameter """ def __init__( self ): return None def __add__(self, n): return n def __sub__(self, n): if type(n) in (types.IntType, types.FloatType, types.LongType, types.ComplexType): return -1 * n raise TypeError def __mul__(self, n): return n def __div__(self, n): if type(n) in (types.IntType, types.FloatType, types.LongType, types.ComplexType): return 1/n raise TypeError def __mod__(self, n): raise TypeError class Dict(dict): def __init__(self): return dict.__init__(self) def __setattr__(self, attr, val): self[attr] = val def __getattr__(self, attr): if attr in self.keys(): return self[attr] return UnknownType() def __getitem__(self, k): if k in self.keys(): return super(Dict, self).__getitem__(k) return UnknownType() if __name__ == '__main__': print "Beginning tests..." h = Dict() print " Object instantiation OK" h['a'] = 1 assert h['a'] == 1, "subscript set/get item" assert h.a == 1, "attribute get" print " Subscript get/set OK" h.a = 2 assert h.a == 2, "attribute set" print " Attribute get/set OK" h.b += 1 assert h.b == 1, "+=" h.c -= 1 assert h.c == -1, "-=" h.d *= 2 assert h.d == 2, "*=" h.e /= 2 assert h.e == 1/2, "/=" try: h.f %= 2 except TypeError: pass print " UnknownType operations OK" # use cases for i in (1,3,1,3,2,1,3,3,4): h[i] += 1 print " Subscript with UnknownType OK" test = { 'a' : 2, 1 : 3, 'c' : -1, 'b' : 1, 'e' : 0, 'd' : 2, 2 : 1, 3 : 4, 4 : 1, } assert test == h, "storage inconsistent!" print " Storage consistency OK" print "All tests passed OK"


You can't default the value of a hash/dict in Python? In Ruby I'd just do this: a = Hash.new(0) a[:k1] += 1 a[:k2] = "blah"

yes, python has defaultdict(), which looks like your ruby in that neither allows us to mix types; we have to pre-commit to a default. And for a super common data type, brackets really reduce readability...

Well, you can mix types in your assignments, but if you don't assign to a particular key you're stuck with that numeric default which is what I assume you mean. - David W