Message246890
I noticed an inconsistency today between the dict() documentation vs. implementation.
The documentation for the dict() built-in [1] states that the function accepts an optional positional argument that is either a mapping object [2] or an iterable object [3].
Consider the following:
import collections.abc
class MyIterable(object):
def __init__(self):
self._data = [('one', 1), ('two', 2)]
def __iter__(self):
return iter(self._data)
class MyIterableWithKeysMethod(MyIterable):
def keys(self):
return "And now for something completely different"
class MyIterableWithKeysAttribute(MyIterable):
keys = "It's just a flesh wound!"
assert issubclass(MyIterable, collections.abc.Iterable)
assert issubclass(MyIterableWithKeysMethod, collections.abc.Iterable)
assert issubclass(MyIterableWithKeysAttribute, collections.abc.Iterable)
assert not issubclass(MyIterable, collections.abc.Mapping)
assert not issubclass(MyIterableWithKeysMethod, collections.abc.Mapping)
assert not issubclass(MyIterableWithKeysAttribute, collections.abc.Mapping)
# OK
assert dict(MyIterable()) == {'one': 1, 'two': 2}
# Traceback (most recent call last):
# File "<stdin>", line 1, in <module>
# TypeError: 'MyIterableWithKeysMethod' object is not subscriptable
assert dict(MyIterableWithKeysMethod()) == {'one': 1, 'two': 2}
# Traceback (most recent call last):
# File "<stdin>", line 1, in <module>
# TypeError: attribute of type 'str' is not callable
assert dict(MyIterableWithKeysAttribute()) == {'one': 1, 'two': 2}
The last two assertions should not fail, and it appears that the offending code can be found in Objects/dictobject.c's dict_update_common:
else if (arg != NULL) {
_Py_IDENTIFIER(keys);
if (_PyObject_HasAttrId(arg, &PyId_keys))
result = PyDict_Merge(self, arg, 1);
else
result = PyDict_MergeFromSeq2(self, arg, 1);
}
PyDict_Merge is used to merge key-value pairs if the optional parameter is a mapping, and PyDict_MergeFromSeq2 is used if the parameter is an iterable.
My immediate thought was to substitute the _PyObject_HasAttrId call with PyMapping_Check which I believe would work in 2.7, but due to #5945 I don't think this fix would work in 3.x.
Thoughts?
[1] https://docs.python.org/3.6/library/stdtypes.html#dict
[2] https://docs.python.org/3.6/glossary.html#term-mapping
[3] https://docs.python.org/3.6/glossary.html#term-iterable |
|
Date |
User |
Action |
Args |
2015-07-18 08:55:00 | christian.barcenas | set | recipients:
+ christian.barcenas, docs@python |
2015-07-18 08:55:00 | christian.barcenas | set | messageid: <1437209700.05.0.411435282726.issue24659@psf.upfronthosting.co.za> |
2015-07-18 08:54:59 | christian.barcenas | link | issue24659 messages |
2015-07-18 08:54:58 | christian.barcenas | create | |
|