Python 'string' % [1, 2, 3] 不会引发 TypeError

标签 python list modulo string-interpolation python-3.2

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 ,因此它不会进入错误情况。

两者 dictlist可以用作此类映射,因此在用作参数时不会引发错误。 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/

相关文章:

language-agnostic - 模运算符 (%) 是如何实际计算的?

javascript - 在 JavaScript 中使用 mod 运算符进行环绕

python - Flask-admin 内联建模传递表单参数抛出 AttributeError

python - 融合几个接近点的简单方法?

python - 句子[:] mean here?]是什么意思

python - 如何对可观察坐标列表进行排序?

jquery - 制作单个选项选择/取消选择列表

Java组合Set和List接口(interface)

arrays - 检查 sub 是否返回 undef

java - Java中长整型除法得到分数