Python 在没有转换说明符的情况下不会在 % 上引发异常
Python not raising an exception on % without conversion specifier
描述了字符串格式的 % 运算符 here。
通常,当出现一个没有转换说明符的字符串时,它会引发一个TypeError: not all arguments converted during string formatting
。例如,"" % 1
将失败。到目前为止,还不错。
有时,它不会失败,但是,如果 % 运算符右边的参数是空的:"" % []
,或 "" % {}
或 "" % ()
将默默地 return 空字符串,看起来很公平。
与 "%s"
相同而不是空字符串会将空对象转换为字符串,除了最后一个会失败,但我认为这是 % 运算符问题的一个实例,它们是用format
方法解决。
还有一个非空字典的情况,如 "" % {"a": 1}
,它之所以有效,是因为它确实应该与命名类型说明符一起使用,如 "%(a)d" % {"a": 1}
.
但是,有一种情况我不明白:"" % b"x"
将 return 空字符串,没有引发异常。为什么?
我不是 100% 确定,但在 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
,但对于位置格式仍然是 "tuplified":
>>> "xxx" % 'a'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: not all arguments converted during string formatting
当然,这个"sequences as mappings"逻辑没有多大意义,因为格式化键总是字符串:
>>> "xxx %(0)s" % ['a']
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: list indices must be integers, not str
但我怀疑有人会解决这个问题,因为 %
无论如何都被放弃了。
违规行位于 unicodeobject.c。它认为所有 "mappings" 的对象,并且明确地不是元组或字符串或其子类,如 "dictionaries",并且如果不是所有参数都被转换,那么它不是错误。
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
和 define that 一样,任何其他带有 __getitem__
的对象也是如此。因此,至少在 Python 3.4 中,具有 __getitem__
的对象不会作为 %
格式操作的右侧参数失败。
现在这是对 Python 2.7 的更改。此外,这样做的原因是没有办法检测所有可能用于 %(name)s
格式化的类型,除非接受 all 实现 __getitem__
,虽然最明显的错误已经被剔除。当 Python 3 发布时,没有人在那里添加 bytes
,尽管它显然不应该支持字符串作为 __getitem__
的参数;但那里也没有list
。
另一个疏忽是列表不能用于格式化位置参数。
描述了字符串格式的 % 运算符 here。
通常,当出现一个没有转换说明符的字符串时,它会引发一个TypeError: not all arguments converted during string formatting
。例如,"" % 1
将失败。到目前为止,还不错。
有时,它不会失败,但是,如果 % 运算符右边的参数是空的:"" % []
,或 "" % {}
或 "" % ()
将默默地 return 空字符串,看起来很公平。
与 "%s"
相同而不是空字符串会将空对象转换为字符串,除了最后一个会失败,但我认为这是 % 运算符问题的一个实例,它们是用format
方法解决。
还有一个非空字典的情况,如 "" % {"a": 1}
,它之所以有效,是因为它确实应该与命名类型说明符一起使用,如 "%(a)d" % {"a": 1}
.
但是,有一种情况我不明白:"" % b"x"
将 return 空字符串,没有引发异常。为什么?
我不是 100% 确定,但在 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
,但对于位置格式仍然是 "tuplified":
>>> "xxx" % 'a'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: not all arguments converted during string formatting
当然,这个"sequences as mappings"逻辑没有多大意义,因为格式化键总是字符串:
>>> "xxx %(0)s" % ['a']
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: list indices must be integers, not str
但我怀疑有人会解决这个问题,因为 %
无论如何都被放弃了。
违规行位于 unicodeobject.c。它认为所有 "mappings" 的对象,并且明确地不是元组或字符串或其子类,如 "dictionaries",并且如果不是所有参数都被转换,那么它不是错误。
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
和 define that 一样,任何其他带有 __getitem__
的对象也是如此。因此,至少在 Python 3.4 中,具有 __getitem__
的对象不会作为 %
格式操作的右侧参数失败。
现在这是对 Python 2.7 的更改。此外,这样做的原因是没有办法检测所有可能用于 %(name)s
格式化的类型,除非接受 all 实现 __getitem__
,虽然最明显的错误已经被剔除。当 Python 3 发布时,没有人在那里添加 bytes
,尽管它显然不应该支持字符串作为 __getitem__
的参数;但那里也没有list
。
另一个疏忽是列表不能用于格式化位置参数。