如果没有转换说明符,Python不会在%上引发异常

时间:2015-02-17 11:03:15

标签: python python-3.x string-formatting

字符串格式的%运算符描述为here

通常,当呈现没有转换说明符的字符串时,它将引发TypeError: not all arguments converted during string formatting。例如,"" % 1将失败。到目前为止,非常好。

但是,有时候,如果%运算符右侧的参数为空,则不会失败:"" % []"" % {}"" % ()将静默返回空字符串,看起来很公平。

"%s"相同而不是空字符串会将空对象转换为字符串,除了最后一个会失败,但我认为这是%运算符问题的一个实例,它由format方法。

还有非空字典的情况,例如"" % {"a": 1},因为它确实应该与命名类型说明符一起使用,例如在"%(a)d" % {"a": 1}中。

但是,有一种情况我不明白:"" % b"x"将返回空字符串,没有异常引发。为什么呢?

2 个答案:

答案 0 :(得分:2)

我不是百分百肯定,但在快速查看sources之后,我猜其原因如下:

%右侧只有一个参数时,Python会查看它是否有getitem方法,如果是,则假定它是一个映射并期望我们使用%(name)s之类的命名格式。否则,Python从参数创建单元素元组并执行位置格式化。不会使用映射检查参数计数,因此,由于byteslists确实有getitem,因此它们不会失败:

>>> "xxx" % b'a'
'xxx'
>>> "xxx" % ['a']
'xxx'

还要考虑:

>>> class X: pass
... 
>>> "xxx" % X()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: not all arguments converted during string formatting

>>> class X:
...    def __getitem__(self,x): pass
... 
>>> "xxx" % X()
'xxx'

字符串是这条规则的例外 - 它们有getitem,但仍然是#t;}&#34; tuplified&#34;用于位置格式化:

>>> "xxx" % 'a'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: not all arguments converted during string formatting

当然,这个&#34;序列作为映射&#34;逻辑没有多大意义,因为格式化键总是字符串:

>>> "xxx %(0)s" % ['a']
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: list indices must be integers, not str

但我怀疑是否有人会解决这个问题,因为%已被放弃了。

答案 1 :(得分:2)

违规行在unicodeobject.c。它将所有对象视为“映射”,并且显式地不是元组或字符串或其子类,作为“字典”,对于那些,如果不是所有参数都被转换则不是错误。

PyMapping_Check定义为:

int
PyMapping_Check(PyObject *o)
{
    return o && o->ob_type->tp_as_mapping &&
        o->ob_type->tp_as_mapping->mp_subscript;
}

也就是说,定义tp_as_mappingmp_subscriptbytes的任何类型都是映射。

__getitem____getitem__一样define that,与%的任何其他对象一样。因此,至少在Python 3.4中,%(name)s的任何对象都不会作为__getitem__格式的右侧参数失败。

现在这是Python 2.7的变化。此外,原因是没有办法检测可用于bytes格式化的所有可能类型,除非接受实现{{1}的所有类型虽然最明显的错误已被取消。当Python 3发布时,没有人在那里添加__getitem__,尽管它显然不应该支持字符串作为list的参数;但那里也没有{{1}}。

另一个疏忽是列表不能用于格式化位置参数。