Add .except() and .only(), and .values_at(). instance methods to dict

I think these are an extremely common needs that are worth having standard methods for. If adding instance methods seems like a bad idea, then maybe add functions to the standard library that perform the same operations. m = {'a': 123, 'b': 456, 'c': 789} m.except(('a', 'c')) # {'b': 456} m.only(('b', 'c')) # {'b': 456, 'c': 789} m.values_at(('a', 'b')) # [123, 456] …or… from mappings import except, only, values_at m = {'a': 123, 'b': 456, 'c': 789} except(m, ('a', 'c')) # {'b': 456} only(m, ('b', 'c')) # {'b': 456, 'c': 789} values_at(m, ('a', 'b')) # [123, 456]

These are all far too easy to do with comprehensions to merit new methods or stdlib functions. m = {'a': 123, 'b': 456, 'c': 789}
m.except(('a', 'c')) # {'b': 456}
m.only(('b', 'c')) # {'b': 456, 'c': 789} m.values_at(('a', 'b')) # [123, 456]
{k:m[v] for k in m if k not in ['a','c']} {k:m[v] for k in m if k in ('b','c')}. [m[k] for k in m if k in {'a', 'b'}] The existing versions are a few characters longer, but far more general when you want minor variations. Moreover, I have very rarely wanted to do ANY of the things described by these hypothetical methods. That said, I think maybe something extending PEP 584 could be reasonable, but I'm not quite sure of the semantics. E.g., we might provide additional set-like operators for dicts.
m | {'a':"Newval"} # We have this now {'a': 'Newval', 'b': 456, 'c': 789}
m - {'a'} # Would rightval be a set or dict though?! {'b': 456, 'c': 789}
m & {'a', 'b'} # Same question, but set feels better, I think {'a': 'Newval', 'b': 456}
-- Keeping medicines from the bloodstreams of the sick; food from the bellies of the hungry; books from the hands of the uneducated; technology from the underdeveloped; and putting advocates of freedom in prisons. Intellectual property is to the 21st century what the slave trade was to the 16th.

David Mertz, Ph.D. writes:
These are all far too easy to do with comprehensions to merit new methods or stdlib functions.
+1
E.g., we might provide additional set-like operators for dicts.
m - {'a'} # Would rightval be a set or dict though?! m & {'a', 'b'} # Same question, but set feels better, I think
Why not "either"? Of course if you allow dicts, the question in both cases becomes whether the presence test is on keys or items. I think keys would be more frequently useful. Steve

On Mon, 6 Jun 2022 at 18:02, Stephen J. Turnbull <stephenjturnbull@gmail.com> wrote:
Or even: Any iterable that yields keys? And then the presence test would be defined entirely by keys (which makes the most sense). You could subtract another dict, or a set, list, or anything. (You could even subtract the string "ab" to remove keys "a" and "b", for what it's worth.) Of note: https://peps.python.org/pep-0584/#what-about-the-full-set-api """This PEP does not take a position on whether dicts should support the full collection of set operators, and would prefer to leave that for a later PEP (one of the authors is interested in drafting such a PEP).""" So if you (or anyone) wants to move forward with this, I would recommend (a) reading the notes in PEP 584, (b) reaching out to all three authors of it to find out their current sentiment, and (c) designing definitive semantics for the operators in question. I'd be happy to help with the mechanics of getting a PEP written. ChrisA

This idea reminds me of numpy fancy indexing: that is, you can pass multiple indexes in at once. (See bumpy docs for details). I have no idea how that might translate to ducts (tulles are perfectly reasonable keys already) but I know I like it in numpy :-) That being said, I can’t remember a use case where I would have used it with dicts. -CHB On Mon, Jun 6, 2022 at 11:04 AM Chris Angelico <rosuav@gmail.com> wrote:
-- Christopher Barker, PhD (Chris) Python Language Consulting - Teaching - Scientific Software Development - Desktop GUI and Web Development - wxPython, numpy, scipy, Cython

On Sun, Jun 05, 2022 at 09:11:41PM -0000, Steve Jorgensen wrote:
Maybe I'm a bit slow because I haven't had my morning coffee yet, but I had to read those three times before I could work out what they actually do. Also because I got thrown by the use of the keyword `except`, and thought initially that this was related to try...except. And lastly because you say that these are extremely common, but I've never used these operations in 20+ years. Or at least not often enough to remember using them, or wishing that they were standard methods. These operations are so simple that it is easier to follow the code than to work out the meaning of the method from the name: # Return a new dict from an old dict with all but a set of keys. new = {key:value for key, value in old.items() if key not in exclusions} # Return a new dict from an old dict with only the included keys. new = {key:value for key, value in old.items() if key in inclusions} # Same as above, but only those matching the included values. new = {key:value for key, value in old.items() if value in inclusions} # Return the values (as a set) of a subset of keys. values = {value for key, value in old.items() if key in inclusions} # Use a list instead, and exclude keys. values = [value for key, value in old.items() if key not in exclusions] # Same, but use a predicate function. values = [value for key, value in old.items() if not predicate(key)] # How about a list of values matching *either* a blacklist of keys # and a whitelist of values, *or* a predicate function on both? values = [value for key, value in old.items() if (key not in blacklist and value in whitelist) or predicate(key, value)] Comprehensions are great! Yes, they take a few extra characters to write, but code is written much more than it is read. Once we have learned comprehensions, it is much easier to read a simple comprehension than to try to decipher a short name like "only". Only what? Only those that match a given list of keys, or values, or a predicate function, or something else? -- Steve

These are all far too easy to do with comprehensions to merit new methods or stdlib functions. m = {'a': 123, 'b': 456, 'c': 789}
m.except(('a', 'c')) # {'b': 456}
m.only(('b', 'c')) # {'b': 456, 'c': 789} m.values_at(('a', 'b')) # [123, 456]
{k:m[v] for k in m if k not in ['a','c']} {k:m[v] for k in m if k in ('b','c')}. [m[k] for k in m if k in {'a', 'b'}] The existing versions are a few characters longer, but far more general when you want minor variations. Moreover, I have very rarely wanted to do ANY of the things described by these hypothetical methods. That said, I think maybe something extending PEP 584 could be reasonable, but I'm not quite sure of the semantics. E.g., we might provide additional set-like operators for dicts.
m | {'a':"Newval"} # We have this now {'a': 'Newval', 'b': 456, 'c': 789}
m - {'a'} # Would rightval be a set or dict though?! {'b': 456, 'c': 789}
m & {'a', 'b'} # Same question, but set feels better, I think {'a': 'Newval', 'b': 456}
-- Keeping medicines from the bloodstreams of the sick; food from the bellies of the hungry; books from the hands of the uneducated; technology from the underdeveloped; and putting advocates of freedom in prisons. Intellectual property is to the 21st century what the slave trade was to the 16th.

David Mertz, Ph.D. writes:
These are all far too easy to do with comprehensions to merit new methods or stdlib functions.
+1
E.g., we might provide additional set-like operators for dicts.
m - {'a'} # Would rightval be a set or dict though?! m & {'a', 'b'} # Same question, but set feels better, I think
Why not "either"? Of course if you allow dicts, the question in both cases becomes whether the presence test is on keys or items. I think keys would be more frequently useful. Steve

On Mon, 6 Jun 2022 at 18:02, Stephen J. Turnbull <stephenjturnbull@gmail.com> wrote:
Or even: Any iterable that yields keys? And then the presence test would be defined entirely by keys (which makes the most sense). You could subtract another dict, or a set, list, or anything. (You could even subtract the string "ab" to remove keys "a" and "b", for what it's worth.) Of note: https://peps.python.org/pep-0584/#what-about-the-full-set-api """This PEP does not take a position on whether dicts should support the full collection of set operators, and would prefer to leave that for a later PEP (one of the authors is interested in drafting such a PEP).""" So if you (or anyone) wants to move forward with this, I would recommend (a) reading the notes in PEP 584, (b) reaching out to all three authors of it to find out their current sentiment, and (c) designing definitive semantics for the operators in question. I'd be happy to help with the mechanics of getting a PEP written. ChrisA

This idea reminds me of numpy fancy indexing: that is, you can pass multiple indexes in at once. (See bumpy docs for details). I have no idea how that might translate to ducts (tulles are perfectly reasonable keys already) but I know I like it in numpy :-) That being said, I can’t remember a use case where I would have used it with dicts. -CHB On Mon, Jun 6, 2022 at 11:04 AM Chris Angelico <rosuav@gmail.com> wrote:
-- Christopher Barker, PhD (Chris) Python Language Consulting - Teaching - Scientific Software Development - Desktop GUI and Web Development - wxPython, numpy, scipy, Cython

On Sun, Jun 05, 2022 at 09:11:41PM -0000, Steve Jorgensen wrote:
Maybe I'm a bit slow because I haven't had my morning coffee yet, but I had to read those three times before I could work out what they actually do. Also because I got thrown by the use of the keyword `except`, and thought initially that this was related to try...except. And lastly because you say that these are extremely common, but I've never used these operations in 20+ years. Or at least not often enough to remember using them, or wishing that they were standard methods. These operations are so simple that it is easier to follow the code than to work out the meaning of the method from the name: # Return a new dict from an old dict with all but a set of keys. new = {key:value for key, value in old.items() if key not in exclusions} # Return a new dict from an old dict with only the included keys. new = {key:value for key, value in old.items() if key in inclusions} # Same as above, but only those matching the included values. new = {key:value for key, value in old.items() if value in inclusions} # Return the values (as a set) of a subset of keys. values = {value for key, value in old.items() if key in inclusions} # Use a list instead, and exclude keys. values = [value for key, value in old.items() if key not in exclusions] # Same, but use a predicate function. values = [value for key, value in old.items() if not predicate(key)] # How about a list of values matching *either* a blacklist of keys # and a whitelist of values, *or* a predicate function on both? values = [value for key, value in old.items() if (key not in blacklist and value in whitelist) or predicate(key, value)] Comprehensions are great! Yes, they take a few extra characters to write, but code is written much more than it is read. Once we have learned comprehensions, it is much easier to read a simple comprehension than to try to decipher a short name like "only". Only what? Only those that match a given list of keys, or values, or a predicate function, or something else? -- Steve
participants (6)
-
Chris Angelico
-
Christopher Barker
-
David Mertz, Ph.D.
-
Stephen J. Turnbull
-
Steve Jorgensen
-
Steven D'Aprano