For what it's worth, I've attached a patch that implements this as a prototype.
It's definitely not mergeable right now. I'm not aware of any issues, but the code needs more error checking, and the coding style doesn't match cpython's. Having said that, it's a 70-line change, so fixing that up should be trivial once the change has some agreement behind it. Steve On Wed, Jul 1, 2020 at 4:31 PM Stestagg <stest...@gmail.com> wrote: > I'll try to unwind the rabbit holes a bit and suggest that the differences > in opinion here boil down to: > > Is it most useful to consider dict_keys/items/values as: (This doesn't > mean what isinstance() returns, or what a previous pep has stated, just how > one thinks about them): > 1. ordered collections that have some set-like operators added for > convenience, (conceptually, if not exactly, class ...(Set, Sequence):) but > are missing a single method full Sequence interface (__getitem__) > or > 2. Sets that happen to have a stable/deterministic element order > > My opinion is that, as of Python 3.7, they are effectively 1, even if the > isinstance hooks haven't been updated. I can see why people may think of > them as 2. I don't have any desire to change minds on this :) > > > Semantic quibbling aside, My opinions/reasoning on the different options > are the following: > > > * Do nothing: > -0.5: I can live with the current situation, some things I do infrequently > will be slightly less convenient, but can work around easily. > > * Add numeric `__getitem__` to `dict_*` view classes: > +1 - This restores code that used to work in python 2, and makese some > things a bit easier. The O(n) behaviour is not ideal, but in my opinion is > an acceptable compromise > > * Add new method to dict class: > -1 This doesn't feel like a good solution to me. Rather than continue to > argue about the justification for why, let's just say its a personal > judgement > > I'm interested in any other opinions on these options > > Steve > > > > On Wed, Jul 1, 2020 at 3:24 PM Steven D'Aprano <st...@pearwood.info> > wrote: > >> On Wed, Jul 01, 2020 at 01:36:34PM +0100, Stestagg wrote: >> > On Wed, Jul 1, 2020 at 12:18 PM Steven D'Aprano <st...@pearwood.info> >> wrote: >> > >> > > On Tue, Jun 30, 2020 at 10:18:34AM +0100, Stestagg wrote: >> > > >> > > > This property is nice, as .keys() (for example) can operate like a >> > > > set and give lots of convenience features. However this doesn't >> > > > really mean that these objects are trying to *be* sets in any >> > > > meaning way, >> > > >> > > Please read the PEP: >> > > >> > > https://www.python.org/dev/peps/pep-3106/ >> > > >> > > It was an explicit design choice to make them be set-like, not an >> > > accident. The whole point was to offer a set interface. >> > >> > >> > I'm pretty familiar with that pep, I'm not sure how knowledge of its >> > contents affects this idea/discussion in any material way. >> >> You: I don't think that dict views are trying to be sets. >> >> Me: Dict views were designed right from the start to be like sets, as >> described in the PEP. >> >> You: I don't think the PEP is relevant. >> >> Okay, whatever you say. >> >> >> > The point being made, was that the utility of trying to shoehorn the >> > dict_* types into a Set pidgeon-hole is quite limited, as practicalities >> > get in the way. >> >> *shrug* >> >> They seem to work pretty well for many uses. Perhaps they could be >> better, but they are what they are, and there's no point pretending that >> they aren't large-s Sets when they clearly are. >> >> py> from collections.abc import Set >> py> isinstance({}.items(), Set) >> True >> >> This is not an accident, it is not a mistake, it wasn't forced on the >> language because dicts used to be unordered. It was an intentional >> design choice. >> >> If you think that *dict views are set-like* being an intentional choice >> is not important to the discussion, what do you consider important? >> >> As far as I am concerned, making dict views into sequences by making >> them indexable should be ruled out. They were intended to be set-like, >> not sequence-like, and that is the end of the story. Adding indexing >> onto them is bolting on an API that was never part of the design. >> >> I still don't think there are any compelling use-cases for indexing >> dicts, but if there are, we should offer a getter method on the dict >> object itself, not try to turn dict views into a hybrid set+sequence. >> Change my mind. >> >> >> > > > I don't think inventing new methods for emulating __getitem__ is >> that >> > > > useful here, there is a well established standard for getting items >> from >> > > > a container by index, I think we should use it :). >> > > >> > > We can't use dict[index] because the index can also be a key: >> > > >> > > d = {'a': None, 'b': None, 0: 999} >> > > d[0] # return 'a' or 999? >> > >> > The currently discussed option is for adding`__getitem__` to the >> > dict_keys/dict_items/dict_values types, *NOT* the base `dict` type. >> >> You: We shouldn't invent a new way of indexing containers. >> >> Also you: Let's invent a new way of indexing containers (subscripting on >> dict views). >> >> So which is it? >> >> Subscripting on the dict object is ruled out because it is ambiguous. >> Here are three possible choices (there may be others): >> >> - Do nothing, we don't need this functionality, there are no compelling >> use-cases for it. Passing dict keys to random.choice is not a >> compelling use-case. >> >> - Add a new interface by making dict views a hybrid set+sequence. >> >> - Add a new getter method on the dict itself. >> >> Aside from the first do-nothing option, one way or the other we're >> adding a new way of indexing the object we actually want to index, >> namely the dict. >> >> The choice is between a getter interface, and a subscript interface on a >> proxy (a dict view). Dicts already offer getter interfaces (dict.get, >> dict.setdefault) so it is not like calling dict methods is unheard of. >> >> >> [...] >> > The point wasn't that: "It's atypical so we can ignore it", but rather: >> > "It's faster/better, but seldom available to be used, here's how we do >> it >> > for the more common case..." >> >> Okay, thanks for the correction. >> >> >> -- >> Steven >> _______________________________________________ >> Python-ideas mailing list -- python-ideas@python.org >> To unsubscribe send an email to python-ideas-le...@python.org >> https://mail.python.org/mailman3/lists/python-ideas.python.org/ >> Message archived at >> https://mail.python.org/archives/list/python-ideas@python.org/message/A34NF6R6JTSQAZLIU6IKN4UWC2L2WANO/ >> Code of Conduct: http://python.org/psf/codeofconduct/ >> >
diff --git a/Objects/dictobject.c b/Objects/dictobject.c index b1f11b3e69..4e5a48f8d5 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -4107,6 +4107,73 @@ dictview_len(_PyDictViewObject *dv) return len; } + +PyObject * _dictview_getitem_ma_unsafe(_PyDictViewObject *dv, size_t index) { + PyObject *result; + PyDictObject *dict = dv->dv_dict; + + if (PyDictKeys_Check(dv)) { + result = DK_ENTRIES(dict->ma_keys)[index].me_key; + Py_INCREF(result); + } + else if (PyDictValues_Check(dv)) + { + result = dict->ma_values[index]; + Py_INCREF(result); + } else { + result = PyTuple_New(2); + PyTuple_SetItem(result, 0, DK_ENTRIES(dict->ma_keys)[index].me_key); + PyTuple_SetItem(result, 1, dict->ma_values[index]); + } + return result; +} + +static PyObject *dv_indexerr = NULL; + +PyObject * dictview_getitem(_PyDictViewObject *dv, Py_ssize_t index) +{ + PyObject *result; + PyDictObject *dict = dv->dv_dict; + + if ((size_t) index >= (size_t) dict->ma_used) { + if (dv_indexerr == NULL) { + dv_indexerr = PyUnicode_FromString( + "index out of range"); + if (dv_indexerr == NULL) + return NULL; + } + PyErr_SetObject(PyExc_IndexError, dv_indexerr); + return NULL; + } + + if (_PyDict_HasSplitTable(dict)) { + return _dictview_getitem_ma_unsafe(dv, index); + } + + PyDictKeyEntry *entry_ptr = DK_ENTRIES(dict->ma_keys); + while(index) { + if (entry_ptr != NULL) { + index--; + } + entry_ptr++; + } + + if (PyDictKeys_Check(dv)) { + result = entry_ptr->me_key; + Py_INCREF(result); + } + else if (PyDictValues_Check(dv)) + { + result = entry_ptr->me_value; + Py_INCREF(result); + } else { + result = PyTuple_New(2); + PyTuple_SetItem(result, 0, entry_ptr->me_key); + PyTuple_SetItem(result, 1, entry_ptr->me_value); + } + return result; +} + PyObject * _PyDictView_New(PyObject *dict, PyTypeObject *type) { @@ -4287,7 +4354,7 @@ static PySequenceMethods dictkeys_as_sequence = { (lenfunc)dictview_len, /* sq_length */ 0, /* sq_concat */ 0, /* sq_repeat */ - 0, /* sq_item */ + (ssizeargfunc)dictview_getitem, /* sq_item */ 0, /* sq_slice */ 0, /* sq_ass_item */ 0, /* sq_ass_slice */
_______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-le...@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/XITJWOM7DI3UIVCDKB4DWBB53DSE4WUV/ Code of Conduct: http://python.org/psf/codeofconduct/