This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

Title: PyObject_GetIter does not behave as documented on dict objects
Type: Stage: resolved
Components: Documentation Versions: Python 3.6
Status: closed Resolution:
Dependencies: Superseder:
Assigned To: docs@python Nosy List: Sam De Meyer, docs@python, rhettinger, serhiy.storchaka
Priority: normal Keywords:

Created on 2017-05-09 20:19 by Sam De Meyer, last changed 2022-04-11 14:58 by admin. This issue is now closed.

Messages (4)
msg293346 - (view) Author: Sam De Meyer (Sam De Meyer) Date: 2017-05-09 20:19
According to the docs ( 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:

>    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)`.
msg293347 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2017-05-09 20:35
I can't believe in this since one-argument iter() just calls PyObject_GetIter().

dict.update() accepts either a dict-like object (which should have the keys() method) or an iterable producing key-value pairs. If your dict-like object doesn't work as expected, check that it has the keys() method.
msg293352 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2017-05-09 21:35
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

    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' %
        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]
                for key, value in other:
                    self[key] = value
        for key, value in kwds.items():
            self[key] = value
msg293456 - (view) Author: Sam De Meyer (Sam De Meyer) Date: 2017-05-10 21:56
You seem to be right, my object was lacking the keys method.

Please excuse me for the false alarm, and thanks for pointing it out.
Date User Action Args
2022-04-11 14:58:46adminsetgithub: 74507
2017-05-10 21:56:50Sam De Meyersetstatus: open -> closed

messages: + msg293456
stage: resolved
2017-05-09 21:35:42rhettingersetnosy: + rhettinger
messages: + msg293352
2017-05-09 20:35:36serhiy.storchakasetnosy: + serhiy.storchaka
messages: + msg293347
2017-05-09 20:19:39Sam De Meyercreate