字符串格式的%运算符描述为here。
通常,当呈现没有转换说明符的字符串时,它将引发TypeError: not all arguments converted during string formatting
。例如,"" % 1
将失败。到目前为止,非常好。
但是,有时候,如果%运算符右侧的参数为空,则不会失败:"" % []
或"" % {}
或"" % ()
将静默返回空字符串,看起来很公平。
与"%s"
相同而不是空字符串会将空对象转换为字符串,除了最后一个会失败,但我认为这是%运算符问题的一个实例,它由format
方法。
还有非空字典的情况,例如"" % {"a": 1}
,因为它确实应该与命名类型说明符一起使用,例如在"%(a)d" % {"a": 1}
中。
但是,有一种情况我不明白:"" % b"x"
将返回空字符串,没有异常引发。为什么呢?
答案 0 :(得分:2)
我不是百分百肯定,但在快速查看sources之后,我猜其原因如下:
当%
右侧只有一个参数时,Python会查看它是否有getitem
方法,如果是,则假定它是一个映射并期望我们使用%(name)s
之类的命名格式。否则,Python从参数创建单元素元组并执行位置格式化。不会使用映射检查参数计数,因此,由于bytes
和lists
确实有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_mapping
且mp_subscript
为bytes
的任何类型都是映射。
而__getitem__
与__getitem__
一样define that,与%
的任何其他对象一样。因此,至少在Python 3.4中,%(name)s
的任何对象都不会作为__getitem__
格式的右侧参数失败。
现在这是Python 2.7的变化。此外,原因是没有办法检测可用于bytes
格式化的所有可能类型,除非接受实现{{1}的所有类型虽然最明显的错误已被取消。当Python 3发布时,没有人在那里添加__getitem__
,尽管它显然不应该支持字符串作为list
的参数;但那里也没有{{1}}。
另一个疏忽是列表不能用于格式化位置参数。