Issue 30322: PyObject_GetIter does not behave as documented on dict objects (original) (raw)
According to the docs (https://docs.python.org/3/c-api/object.html) the PyObject_GetIter
method should be equivalent to the python call iter(<some_dict>)
, but, when given a dict, the PyObject_GetIter
returns an iterator over key-value pairs whereas the iter()
method returns an iterator over keys only.
I tripped over this when giving the <some_dict>.update()
a dict-like object that does not inherit from the builtin dict and implements its own __iter__()
.
The update()
method eventually reaches the following piece of code:
https://hg.python.org/cpython/file/4243df51fe43/Objects/dictobject.c#l2383
it = PyObject_GetIter(seq2); ... item = PyIter_Next(it); ... fast = PySequence_Fast(item, ""); ... key = PySequence_Fast_GET_ITEM(fast, 0); value = PySequence_Fast_GET_ITEM(fast, 1);
displaying the difference in behaviour between PyObject_GetIter
and iter(o)
.
This from the help on dict.update():
| update(...) | D.update([E, ]**F) -> None. Update D from dict/iterable E and F. | If E present and has a .keys() method, does: for k in E: D[k] = E[k] | If E present and lacks .keys() method, does: for (k, v) in E: D[k] = v | In either case, this is followed by: for k in F: D[k] = F[k]
Likewise in the source for collections.abc.MutableMapping:
def update(*args, **kwds):
''' D.update([E, ]**F) -> None. Update D from mapping/iterable E and F.
If E present and has a .keys() method, does: for k in E: D[k] = E[k]
If E present and lacks .keys() method, does: for (k, v) in E: D[k] = v
In either case, this is followed by: for k, v in F.items(): D[k] = v
'''
if not args:
raise TypeError("descriptor 'update' of 'MutableMapping' object "
"needs an argument")
self, *args = args
if len(args) > 1:
raise TypeError('update expected at most 1 arguments, got %d' %
len(args))
if args:
other = args[0]
if isinstance(other, Mapping):
for key in other:
self[key] = other[key]
elif hasattr(other, "keys"):
for key in other.keys():
self[key] = other[key]
else:
for key, value in other:
self[key] = value
for key, value in kwds.items():
self[key] = value