wim*_*wim 16 python python-3.x
据推测,dict_keys应该表现为类似集合的对象,但它们缺少difference方法,并且减法行为似乎有所不同.
>>> d = {0: 'zero', 1: 'one', 2: 'two', 3: 'three'}
>>> d.keys() - [0, 2]
{1, 3}
>>> d.keys() - (0, 2)
TypeError: 'int' object is not iterable
Run Code Online (Sandbox Code Playgroud)
为什么dict_keys类试图在这里迭代一个整数?这不违反鸭子打字吗?
>>> dict.fromkeys(['0', '1', '01']).keys() - ('01',)
{'01'}
>>> dict.fromkeys(['0', '1', '01']).keys() - ['01',]
{'1', '0'}
Run Code Online (Sandbox Code Playgroud)
Sha*_*ger 17
这看起来像是一个bug.实现是将转换dict_keys为a set,然后调用.difference_update(arg)它.
看起来它们被误用_PyObject_CallMethodId(优化的变体PyObject_CallMethod),通过传递一个格式字符串"O".事情是,PyObject_CallMethod并且记录了朋友需要一个Py_BuildValue"应该产生tuple" 的格式字符串.对于多个格式代码,它会tuple自动包装值,但只有一个格式代码,它不会tuple,它只是创建值(在这种情况下,因为它已经PyObject*,它所做的只是递增引用计数) .
虽然我没有追踪它可能在哪里做这个,但我怀疑在内部的某个地方,它识别CallMethod不产生a tuple并将它们包装成一个元素tuple的调用,因此被调用的函数实际上可以接收预期格式的参数.当减去a时tuple,它已经是a了tuple,这个修复代码永远不会激活; 当list它通过时,它确实成为tuple包含该元素的一个元素list.
difference_update采取varargs(就好像它被宣布def difference_update(self, *args)).因此,当它接收到解包时tuple,它认为它应该从每个条目中减去元素tuple,而不是将所述条目视为要减去自己的值.为了说明,当你这样做时:
mydict.keys() - (1, 2)
Run Code Online (Sandbox Code Playgroud)
这个bug导致它(粗略地):
result = set(mydict)
# We've got a tuple to pass, so all's well...
result.difference_update(*(1, 2)) # Unpack behaves like difference_update(1, 2)
# OH NO!
Run Code Online (Sandbox Code Playgroud)
而:
mydict.keys() - [1, 2]
Run Code Online (Sandbox Code Playgroud)
作用:
result = set(mydict)
# [1, 2] isn't a tuple, so wrap
result.difference_update(*([1, 2],)) # Behaves like difference_update([1, 2])
# All's well
Run Code Online (Sandbox Code Playgroud)
这就是为什么tuple的str作品(错误地),- ('abc', '123')就是执行呼叫相当于:
result.difference_update(*('abc', '123'))
# or without unpacking:
result.difference_update('abc', '123')
Run Code Online (Sandbox Code Playgroud)
而且由于strs为他们的角色的iterables,它只是轻率地删除条目'a','b','c',等来代替'abc',并'123'喜欢你的预期.
基本上,这是一个错误(当我有机会的时候),我会把它归咎于CPython人.
可能应该调用正确的行为(假设Id此API存在此变体):
_PyObject_CallMethodObjArgsId(result, &PyId_difference_update, other, NULL);
Run Code Online (Sandbox Code Playgroud)
它根本没有包装问题,并且可以更快地启动; 最小的变化是将格式字符串更改"(O)"为强制tuple创建,即使对于单个项目也是如此,但由于格式字符串不会获得任何效果,_PyObject_CallMethodObjArgsId因此更好.
| 归档时间: |
|
| 查看次数: |
476 次 |
| 最近记录: |