Nested Mapping
Steven D'Aprano
steve at REMOVE-THIS-cybersource.com.au
Sat Oct 23 01:11:55 EDT 2010
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
More information about the Python-list
mailing list