On Thu, 21 Oct 2010 16:19:43 -0700, Raymond Hettinger wrote: > I'm considering a nested mapping class for the collections module and > would like to solicit feedback from people here on comp.lang.python: > > http://code.activestate.com/recipes/577434-nested-contexts-a-chain- of-mapping-objects/
Very nice! The emulation of Python's nested scopes isn't perfect: >>> c = Context() >>> c['spam'] = 'global' >>> c = c.new_child() >>> c['spam'] = 'local' >>> c['spam'] 'local' >>> del c['spam'] >>> c['spam'] 'global' So far so good -- that is exactly what I would expect. But: >>> spam = "global" >>> def f(): ... spam = "local" ... print spam ... del spam ... print spam ... >>> f() local Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 5, in f UnboundLocalError: local variable 'spam' referenced before assignment I'd call that a gotcha in CPython rather than a problem with the Context class. But it might be worth a note in the documentation somewhere. [...] > The API seeks to fully emulate regular dictionaries. It doesn't appear to do so as yet. E.g. update is missing, although you refer to it in the comments. I don't think that either iteration or len are right. I would expect that these should only count unique keys, not repeated keys. Given: c = Context() c['spam'] = 'global' c = c.new_child() c['spam'] = 'local' I would expect that iteration should yield 'spam' once rather than twice, and c.items() should yield ('spam', 'local') rather than either: ('spam', 'local'), ('spam', 'global') ('spam', 'local'), ('spam', 'local') For the rare(?) cases where users do want to see non-unique keys, it's trivial enough to get, given that Context.maps is public. Simple enough to do in place: for key in chain.from_iterable(c.maps): process(key) With the current API, getting unique keys is less simple: seen = set() for key in c: if key in seen: continue seen.add(seen) process(key) I think the API should be based on unique keys, and leave getting non- unique keys to the caller. > I would appreciate any feedback on the API and on how well it fits with > various use cases that you've found in the wild. I can see myself using it in the future. I'd like to see the constructor use the same signature as regular dicts. Instead of: config = Context() config.update({'spam':1, 'cheese':0}, parrot='sleeping') I should be able to pass a dict or iterable of (key,item) pairs, plus keyword arguments, to initialise the newly created scope: config = Context({'spam':1, 'cheese':0}, parrot='sleeping') And similarly for new_child(). I think this would be more useful to me than the current arguments taken by the constructor. I don't see myself needing enable_nonlocal often enough to want a shortcut for >>> config = Context() >>> config.enable_nonlocal = True and as for parent, there doesn't seem to me to be any point to having it as an argument to the constructor. Instead of: >>> top = Context() >>> bottom = Context(parent=top) you can say: >>> top = Context() >>> bottom = top.new_child() Neither is particularly simpler than the other. -- Steven -- http://mail.python.org/mailman/listinfo/python-list