是 str.__mod__
的确切行为记录在案?
这两行代码按预期工作:
>>> 'My number is: %s.' % 123
'My number is: 123.'
>>> 'My list is: %s.' % [1, 2, 3]
'My list is: [1, 2, 3].'
此行也按预期运行:
>>> 'Not a format string' % 123
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: not all arguments converted during string formatting
但是这行是什么以及为什么它不会引发任何错误?
>>> 'Not a format string' % [1, 2, 3]
'Not a format string'
P.S.
>>> print(sys.version)
3.3.2 (default, Aug 15 2013, 23:43:52)
[GCC 4.7.3]
最佳答案
我认为负责的行可以在 CPython 源代码中找到,我得到了 git v3.8.2
:
在函数中
PyObject *
PyUnicode_Format(PyObject *format, PyObject *args)
在
Objects/unicodeobject.c
,第14944行,有以下几行Objects/unicodeobject.c
, 第 15008 行if (ctx.argidx < ctx.arglen && !ctx.dict) {
PyErr_SetString(PyExc_TypeError,
"not all arguments converted during string formatting");
goto onError;
}
如果
arglen
,这将给出错误不匹配,但如果 ctx.dict
不会报错是真的”。什么时候是“真的”?Objects/unicodeobject.c
, 第 14976 行if (PyMapping_Check(args) && !PyTuple_Check(args) && !PyUnicode_Check(args))
ctx.dict = args;
else
ctx.dict = NULL;
好的,
PyMapping_Check
检查通过的 args
,如果这是“真”,并且我们没有元组或 unicode 字符串,我们设置 ctx.dict = args
.什么
PyMapping_Check
做?Objects/abstract.c
, 第 2110 行int
PyMapping_Check(PyObject *o)
{
return o && o->ob_type->tp_as_mapping &&
o->ob_type->tp_as_mapping->mp_subscript;
}
根据我的理解,如果该对象可以用作“映射”,并且可以索引/下标,这将返回
1
.在这种情况下,ctx.dict
的值将设置为 args
, 即 !0
,因此它不会进入错误情况。两者
dict
和 list
可以用作此类映射,因此在用作参数时不会引发错误。 tuple
在第 14976 行的检查中被明确排除,可能是因为它用于将可变参数传递给格式化程序。我不清楚这种行为是否或为什么是故意的,但源代码中的部分没有注释。
基于此,我们可以尝试:
assert 'foo' % [1, 2] == 'foo'
assert 'foo' % {3: 4} == 'foo'
class A:
pass
assert 'foo' % A() == 'foo'
# TypeError: not all arguments converted during string formatting
class B:
def __getitem__(self):
pass
assert 'foo' % B() == 'foo'
因此,对象具有
__getitem__
就足够了。方法定义为不触发错误。编辑:在
v3.3.2
,这是在 OP 中引用的,违规行是同一文件中的第 13922、13459 和 1918 行,逻辑看起来相同。EDIT2:在
v3.0
,检查位于 Objects/unicodeobject.c
中的第 8841 行和第 9226 行, PyMapping_Check
来自 Objects/abstract.c
尚未在 Unicode 格式代码中使用。EDIT3:根据一些二分法和 git 责备,核心逻辑(在 ASCII 字符串上,而不是 unicode 字符串上)可以追溯到 Python 1.2,并由 GvR 自己在 25 年前实现:
commit caeaafccf7343497cc654943db09c163e320316d
Author: Guido van Rossum <guido@python.org>
Date: Mon Feb 27 10:13:23 1995 +0000
don't complain about too many args if arg is a dict
diff --git a/Objects/stringobject.c b/Objects/stringobject.c
index 7df894e12c..cb76d77f68 100644
--- a/Objects/stringobject.c
+++ b/Objects/stringobject.c
@@ -921,7 +921,7 @@ formatstring(format, args)
XDECREF(temp);
} /* '%' */
} /* until end */
- if (argidx < arglen) {
+ if (argidx < arglen && !dict) {
err_setstr(TypeError, "not all arguments converted");
goto error;
}
可能 GvR 可以告诉我们为什么这是预期行为。
关于Python 'string' % [1, 2, 3] 不会引发 TypeError,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18930647/