这似乎是一个错误。实现是将转换
dict_keys为
set,然后对其进行调用
.difference_update(arg)。
通过传递格式字符串just
,看来它们被滥用
_PyObject_CallMethodId(是的优化变体
PyObject_CallMethod)
"O"。事实是这样,
PyObject_CallMethod并且记录在案的朋友要求使用
Py_BuildValue“应该产生一个
tuple”的格式字符串。使用一个以上的格式代码,它会
tuple自动将值包装,而使用一个格式的代码,它不会
tuple,它只是创建值(在这种情况下,因为它已经
PyObject*,它所做的就是增加引用计数)
。
虽然我还没有找到执行此操作的位置,但我怀疑内部结构中的某个位置正在识别
CallMethod不会产生a的调用
tuple并将它们包装成一个元素,
tuple因此被调用函数实际上可以按预期格式接收参数。减去时
tuple,它已经是
tuple,并且此修复代码永远不会激活;传递a时
list,它的确成为一个
tuple包含的元素
list。
difference_update接受varargs(就像声明了一样
def difference_update(self,*args))。因此,当它收到未包装的内容时
tuple,它认为应该从中的每个条目减去元素
tuple,而不是将所述条目视为要减去自身的值。为了说明,当您这样做:
mydict.keys() - (1, 2)
该错误导致它执行(大致):
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!
而:
mydict.keys() - [1, 2]
确实:
result = set(mydict)# [1, 2] isn't a tuple, so wrapresult.difference_update(*([1, 2],)) # Behaves like difference_update([1, 2])# All's well
这就是a
tupleof
strwork(错误地)
- ('abc', '123')执行等同于以下操作的原因的原因:result.difference_update(*('abc', '123'))# or without unpacking:result.difference_update('abc', '123')而且由于
strs为他们的角色的iterables,它只是轻率地删除条目
'a',
'b',
'c',等来代替
'abc',并
'123'令您满意。
基本上,这是一个错误;它是针对CPython的人员提出的,并已在3.6.0(以及2.7、3.4和3.5的更高版本)中修复。
可能应该调用了正确的行为(假设
Id此API存在此变体):
_PyObject_CallMethodObjArgsId(result, &PyId_difference_update, other, NULL);
完全没有打包问题,并且启动速度更快;最小的更改是将格式字符串更改为即使是单个项目也
"(O)"可以强制
tuple创建,但是由于格式字符串没有任何收益,
_PyObject_CallMethodObjArgsId因此更好。



