Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make ChainMap() public in the collections module. #55506

Closed
rhettinger opened this issue Feb 23, 2011 · 11 comments
Closed

Make ChainMap() public in the collections module. #55506

rhettinger opened this issue Feb 23, 2011 · 11 comments
Assignees
Labels
type-feature A feature request or enhancement

Comments

@rhettinger
Copy link
Contributor

BPO 11297
Nosy @rhettinger, @pitrou, @merwok, @alex, @bitdancer, @durban
Files
  • chainmap.diff: Documentation patch
  • Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

    Show more details

    GitHub fields:

    assignee = 'https://github.com/rhettinger'
    closed_at = <Date 2011-02-26.01:08:11.548>
    created_at = <Date 2011-02-23.10:22:35.821>
    labels = ['type-feature']
    title = 'Make ChainMap() public in the collections module.'
    updated_at = <Date 2011-02-26.01:20:32.856>
    user = 'https://github.com/rhettinger'

    bugs.python.org fields:

    activity = <Date 2011-02-26.01:20:32.856>
    actor = 'eric.araujo'
    assignee = 'rhettinger'
    closed = True
    closed_date = <Date 2011-02-26.01:08:11.548>
    closer = 'rhettinger'
    components = []
    creation = <Date 2011-02-23.10:22:35.821>
    creator = 'rhettinger'
    dependencies = []
    files = ['20858']
    hgrepos = []
    issue_num = 11297
    keywords = ['patch']
    message_count = 11.0
    messages = ['129161', '129162', '129163', '129216', '129217', '129218', '129220', '129439', '129482', '129483', '129484']
    nosy_count = 6.0
    nosy_names = ['rhettinger', 'pitrou', 'eric.araujo', 'alex', 'r.david.murray', 'daniel.urban']
    pr_nums = []
    priority = 'low'
    resolution = 'fixed'
    stage = None
    status = 'closed'
    superseder = None
    type = 'enhancement'
    url = 'https://bugs.python.org/issue11297'
    versions = ['Python 3.3']

    @rhettinger
    Copy link
    Contributor Author

    Attaching a documentation patch.

    @rhettinger rhettinger self-assigned this Feb 23, 2011
    @rhettinger rhettinger added the type-feature A feature request or enhancement label Feb 23, 2011
    @pitrou
    Copy link
    Member

    pitrou commented Feb 23, 2011

    This is nice, but IMO there is some information lacking, e.g.:

    • when an underlying mapping is mutated, does the ChainMap get updated too?
    • does it work with arbitrary mappings or only with dicts or dicts subclasses?

    I think new_child() isn't very useful. It seems two specialized for a one-liner. Ditto for parents().

    @pitrou
    Copy link
    Member

    pitrou commented Feb 23, 2011

    ("too specialized", sorry)

    @bitdancer
    Copy link
    Member

    I don't think that new_child and parents are too specialized at all, indeed they are essential to one of the primary use cases for the construct. I find Django's push and pop much more intuitive than new_child and parents, however, and would prefer those methods.

    @alex
    Copy link
    Member

    alex commented Feb 23, 2011

    An important distinction with Django's push/pop is that they mutate the Context (ChainMap) rather than return a fresh instance.

    @bitdancer
    Copy link
    Member

    Yes, that's part of what I find more intuitive about it. I think of the chainmap as a stack. Perhaps if I had a different application (I would use it for either configuration or namespace management) I'd want a different API, but for those two the stack approach seems most natural to me.

    In particular, since only the top dict can be updated, it seems most natural to pop it off the top of the stack in order to modify the next one down (and then push it back, if desired). If instead the way to modify the next one down is to do parents, then I'm mutating the chainmap I just did the parents call on, but I'm not referencing that object, I'm referencing the one I got back from the parents call. It just seems more natural that the mutation operations should be carried out via a single chainmap object by using pop and push rather than effectively modifying (potentially multiple) chainmap objects by manipulating other chainmap objects. (Yes, I realize that it is really the underlying dicts that are being modified, but conceptually I'm thinking of the chainmap as a single data structure).

    @rhettinger
    Copy link
    Contributor Author

    FWIW, the new_child() and parents() part of the API was modeled after contexts in ANLTR where they are needed to overcome the limitations of Django's push/pop style which precludes a context from having multiple, independent children at the same time. The module docstring in the http://code.activestate.com/recipes/577434/ recipe shows how new_child() can be used to easily model both dynamic scoping and nested scoping.

    The other advantage of the new_child/parents API over the push/pop API is that it overcomes the occasional templating need to keep two copies of the context (before a push and after a push).

    In some ways, it is more difficult to keep track of a mutating chain that is being continuously pushed and popped. It is simpler to assign a chain to a variable and always know that it is associated with a given template and not have to worry about whether some utility function pushed a new context and failed to pop it when it was done. A push/pop style introduces the same problems as matching matching malloc() with free() in C.

    @merwok
    Copy link
    Member

    merwok commented Feb 25, 2011

    Minor doc issue: s/builtin/builtins/

    @rhettinger
    Copy link
    Contributor Author

    Antoine. Thanks. I put in a paragraph re-emphasizing that ChainMap is a view and that changes in the underlying mappings get reflected in the ChainMap. Also, the first sentence says that ChainMap groups multiple dicts or other mappings. So, any mapping-like object will work.

    Éric, I changed built-in to builtin. It is used as an adjetive, not as a module reference (that's the usual practice when referring the builtin functions).

    See r88628.

    @rhettinger
    Copy link
    Contributor Author

    I was thinking of adding a recipes section to show how to extend or override the class:

    class DjangoContext(ChainMap):
       def push(self):
           self.maps.insert(0, {})
       def pop(self):
           self.maps.pop(0)
    
    class NestedScope(ChainMap):
       'Mutating methods that write to first matching dict'
    
        def __setitem__(self, key, value):
            '''Find the first matching *key* in chain and set its value.
            If not found, sets in maps[0].
        '''
        for m in self.maps:
            if key in m:
                break
        else:
            m = self.maps[0]
        try:
            cs = m.chain_set
        except AttributeError:
            m[key] = value
        else:
            cs(key, value)
    
        def __delitem__(self, key):
            '''Find and delete the first matching *key* in the chain.
            Raise KeyError if not found.
        '''
        for m in self.maps:
            if key in m:
                break
        try:
            cd = m.chain_del
        except AttributeError:
            del m[key]
        else:
            cd(key)
    
        def popitem(self):
            for m in self.maps:
                if m:
                    break
            return m.popitem()
    
        def clear(self):
            for m in self.maps:
                m.clear()

    @merwok
    Copy link
    Member

    merwok commented Feb 26, 2011

    Raymond: Sorry I was imprecise. I was referring specifically to “import __builtin__” in collections.rst.

    @ezio-melotti ezio-melotti transferred this issue from another repository Apr 10, 2022
    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
    Labels
    type-feature A feature request or enhancement
    Projects
    None yet
    Development

    No branches or pull requests

    5 participants