Re: [Python-ideas] Add dict.append and dict.extend

I think your proposal got a bit confused by the choice of names, and that you were proposing two things, one of which I think already exists (setdefault). So, I _think_ what you are proposing is that there be a method something like: def exclusive_add(self, key, value): if key in self: raise KeyError("the key: {} already exists in this dict".format(key)) self[key] = value Which would make sense if that is a common use case, and you make a pretty good case for that, with the analogy to UNIQUE in database tables. I will point out that the DB case is quite different, because python dicts have a way to of spelling it that's really pretty straightforward, performant and robust. However, .setdefault() is essentially a replacement for: if key in a_dict: value = a_dict(key) else: a_dict[key] = default_value value = default_value or a similar try: except block: try: value = a_dict[key] except KeyError: a_dict[key] = default_value value = default_value And I'm glad it's there. So - -if this is a fairly common use case, then maybe it's worth adding. Note that this would be adding a new method to not just dict, but to the entire mapping protocol (ABC) -- so maybe a heavier lift than you're imagining. -CHB PS: making sure this is what you are suggesting: In [*12*]: *class* *my_dict*(dict): ...: *def* exclusive_add(self, key, value): ...: *if* key *in* self: ...: *raise* *KeyError*("the key: *{}* already exists in this dict".format(key)) ...: self[key] = value ...: In [*13*]: d = my_dict() In [*14*]: d.exclusive_add('this', 5) In [*15*]: d.exclusive_add('that', 6) In [*16*]: d.exclusive_add('this', 7) --------------------------------------------------------------------------- KeyError Traceback (most recent call last) <ipython-input-16-b7600f2469fb> in <module>() ----> 1 d.exclusive_add('this', 7) <ipython-input-12-5f1c0f0709c6> in exclusive_add(self, key, value) * 2* def exclusive_add(self, key, value): * 3* if key in self: ----> 4 raise KeyError("the key: {} already exists in this dict".format(key)) * 5* self[key] = value * 6* KeyError: 'the key: this already exists in this dict' -- Christopher Barker, Ph.D. Oceanographer Emergency Response Division NOAA/NOS/OR&R (206) 526-6959 voice 7600 Sand Point Way NE (206) 526-6329 fax Seattle, WA 98115 (206) 526-6317 main reception Chris.Barker@noaa.gov

On Tue, Jun 05, 2018 at 09:44:59AM -0700, Chris Barker via Python-ideas wrote:
Ben's very first paragraph in this thread says: I'd like to propose adding `append` and `extend` methods to dicts which behave like `__setitem__` and `update` respectively, except that they raise an exception (KeyError?) instead of overwriting preexisting entries. setdefault does not raise an exception. So while your enthusiasm does you credit (setdefault is an excellent little method that is not known anywhere near as well as it ought to be) I don't think it is the least bit relevant here. Ben's proposal is for something like your exclusive_add below (although written in C so it would be atomic), and then given that, something like: def exclusive_update(self, other): # simplified version for key in other: self.exclusive_add(key, other[key]) except that it isn't clear to me what should happen if a key matches: 1) should the update have an "all or nothing" guarantee? (either the entire update succeeds, or none of it) 2) if not, what happens on partial success? are the results dependent on the order of the attempted update?
I'm not sure that it is so common. I think I've wanted the opposite behaviour more often: aside from initialisation, only allow setting existing keys, and raise if the key doesn't exist. Nor do I think the DB analogy is very convincing, since we have no transactions with ACID guarantees for dicts. Pretty much the only similarity between a dict and a DB is that they both map a key to a value.
I'm confused... first you say that Ben makes a good case for this functionality with the DB analogy, and then one sentence later, you say the DB case is very different. So not a good case? I don't understand. And what is this way of spelling "it" (what is it?) that's straightforward and robust? You've completely lost me, sorry. -- Steve

On Tue, Jun 5, 2018 at 4:10 PM, Steven D'Aprano <steve@pearwood.info> wrote:
I wasn't trying to make a case either way -- on the one hand, there is a good analogy to DB UNIDQUE, on the other hand, dicts are really pretty different than DBs.
And what is this way of spelling "it" (what is it?) that's straightforward and robust? You've completely lost me, sorry.
if key in dict: raise KeyError if you had to do that with a DB before adding a record, it could be a pretty expensive operation.... Thinking on this a bit more, I'm pretty -1 -- the main reason that if we had a dict.exclusive_add() method, when you wanted to use it, you'd have to catch the KeyError and do something: try: my_dict.exclusive_add(key, val) except KeyError: do_something_else_probably_with(my_dict, val) since you'd have to catch it, then you aren't really simplifying the code much anyway: if key in my_dict: so_something_else else: my_dict[key] = val the same amount of code and I think the explicit check is clearer.... Also -- seems kind of odd to raise a KeyError when the key IS there?!? -CHB
-- Christopher Barker, Ph.D. Oceanographer Emergency Response Division NOAA/NOS/OR&R (206) 526-6959 voice 7600 Sand Point Way NE (206) 526-6329 fax Seattle, WA 98115 (206) 526-6317 main reception Chris.Barker@noaa.gov

On Tue, Jun 05, 2018 at 09:44:59AM -0700, Chris Barker via Python-ideas wrote:
Ben's very first paragraph in this thread says: I'd like to propose adding `append` and `extend` methods to dicts which behave like `__setitem__` and `update` respectively, except that they raise an exception (KeyError?) instead of overwriting preexisting entries. setdefault does not raise an exception. So while your enthusiasm does you credit (setdefault is an excellent little method that is not known anywhere near as well as it ought to be) I don't think it is the least bit relevant here. Ben's proposal is for something like your exclusive_add below (although written in C so it would be atomic), and then given that, something like: def exclusive_update(self, other): # simplified version for key in other: self.exclusive_add(key, other[key]) except that it isn't clear to me what should happen if a key matches: 1) should the update have an "all or nothing" guarantee? (either the entire update succeeds, or none of it) 2) if not, what happens on partial success? are the results dependent on the order of the attempted update?
I'm not sure that it is so common. I think I've wanted the opposite behaviour more often: aside from initialisation, only allow setting existing keys, and raise if the key doesn't exist. Nor do I think the DB analogy is very convincing, since we have no transactions with ACID guarantees for dicts. Pretty much the only similarity between a dict and a DB is that they both map a key to a value.
I'm confused... first you say that Ben makes a good case for this functionality with the DB analogy, and then one sentence later, you say the DB case is very different. So not a good case? I don't understand. And what is this way of spelling "it" (what is it?) that's straightforward and robust? You've completely lost me, sorry. -- Steve

On Tue, Jun 5, 2018 at 4:10 PM, Steven D'Aprano <steve@pearwood.info> wrote:
I wasn't trying to make a case either way -- on the one hand, there is a good analogy to DB UNIDQUE, on the other hand, dicts are really pretty different than DBs.
And what is this way of spelling "it" (what is it?) that's straightforward and robust? You've completely lost me, sorry.
if key in dict: raise KeyError if you had to do that with a DB before adding a record, it could be a pretty expensive operation.... Thinking on this a bit more, I'm pretty -1 -- the main reason that if we had a dict.exclusive_add() method, when you wanted to use it, you'd have to catch the KeyError and do something: try: my_dict.exclusive_add(key, val) except KeyError: do_something_else_probably_with(my_dict, val) since you'd have to catch it, then you aren't really simplifying the code much anyway: if key in my_dict: so_something_else else: my_dict[key] = val the same amount of code and I think the explicit check is clearer.... Also -- seems kind of odd to raise a KeyError when the key IS there?!? -CHB
-- Christopher Barker, Ph.D. Oceanographer Emergency Response Division NOAA/NOS/OR&R (206) 526-6959 voice 7600 Sand Point Way NE (206) 526-6329 fax Seattle, WA 98115 (206) 526-6317 main reception Chris.Barker@noaa.gov
participants (3)
-
Chris Angelico
-
Chris Barker
-
Steven D'Aprano