使用Python的format()方法打印布尔值True / False

22

我想打印一个布尔表达式的真值表,在此过程中,我偶然发现了以下内容:

>>> format(True, "") # shows True in a string representation, same as str(True)
'True'
>>> format(True, "^") # centers True in the middle of the output string
'1'

一旦我指定了格式说明符,format() 就会将 True 转换为 1。我知道 boolint 的子类,所以 True 会被评估为 1

>>> format(True, "d") # shows True in a decimal format
'1'

但是,为什么在第一个示例中使用格式说明符会将'True'更改为1

我查看了文档以获取澄清。它唯一说的是:

一般约定是,空的格式字符串("")产生的结果与对该值调用str()相同。非空格式字符串通常会修改结果。

因此,在使用格式说明符时字符串会被修改。但是,如果只有指定对齐运算符(例如^)),为什么会从True更改为1呢?


1
一般的约定是,一个空的格式字符串("")产生的结果与您调用值的str()函数的结果相同。一个非空的格式字符串通常会修改结果。 - netcoder
我不知道为什么要这样做,但如果你想修复它,你可以执行 format(str(True),"^") - jspurim
谢谢,我已经修复了,但我只是好奇它的“为什么” :) - Stefan van den Akker
我的猜测是这与“^”运算符的隐式转换有关,尽管这很奇怪。还要注意,“^”是居中对齐运算符,而不是宽度运算符。 - aruisdante
是的,这很奇怪。已经更正了问题。 - Stefan van den Akker
1个回答

9
优秀的问题!我相信我有答案。这需要在C语言中深入研究Python源代码,所以请耐心等待。
首先,format(obj, format_spec)只是obj.__format__(format_spec)的语法糖。要具体查看此处发生的位置,您需要查看abstract.c中的函数。
PyObject *
PyObject_Format(PyObject* obj, PyObject *format_spec)
{
    PyObject *empty = NULL;
    PyObject *result = NULL;

    ...

    if (PyInstance_Check(obj)) {
        /* We're an instance of a classic class */
HERE -> PyObject *bound_method = PyObject_GetAttrString(obj, "__format__");
        if (bound_method != NULL) {
            result = PyObject_CallFunctionObjArgs(bound_method,
                                                  format_spec,
                                                  NULL);

    ...
}

为了找到确切的调用,我们需要查看intobject.c文件:intobject.c
static PyObject *
int__format__(PyObject *self, PyObject *args)
{
    PyObject *format_spec;

    ...

    return _PyInt_FormatAdvanced(self,
                     ^           PyBytes_AS_STRING(format_spec),
                     |           PyBytes_GET_SIZE(format_spec));
               LET'S FIND THIS
    ...
}

_PyInt_FormatAdvanced 实际上是在 formatter_string.c 中被定义为宏,在 formatter.h 中作为函数存在:

static PyObject*
format_int_or_long(PyObject* obj,
               STRINGLIB_CHAR *format_spec,
           Py_ssize_t format_spec_len,
           IntOrLongToString tostring)
{
    PyObject *result = NULL;
    PyObject *tmp = NULL;
    InternalFormatSpec format;

    /* check for the special case of zero length format spec, make
       it equivalent to str(obj) */
    if (format_spec_len == 0) {
        result = STRINGLIB_TOSTR(obj);   <- EXPLICIT CAST ALERT!
        goto done;
    }

    ... // Otherwise, format the object as if it were an integer
}

因此,你的答案就在这里。简单检查format_spec_len是否为0,如果是,则将obj转换为字符串。正如你所知道的那样,str(True)'True',谜底揭开了!


4
我认为提问者想知道为什么 '^'.format(True) == '1',而不是为什么 ''.format(True) == 'True'。或者更准确地说,为什么前者会隐式转换为 int,而后者则正确评估。 - aruisdante
2
在Python中不是这样的。>>> type(True) = <type 'bool'>,而且type(True) == type(1) == False。在C解释器的底层C代码中,它显然被转换为int,因为C缺乏bool原始类型,但这并不能解释在Python端的隐式转换。 - aruisdante
再说一遍,这并不能解释隐式转换。>>> 'The Bool is {}'.format(True) == 'The Bool is True'。它似乎只在你将参数指定到{}时才会这样做,例如'The Bool is {:^}'.format(True) == 'The Bool is 1',但是'The Bool is {!r:^}'.format(True) == 'The Bool is True' - aruisdante
2
澄清一下:由于boolint的子类,但bool没有指定自己的__format__方法,因此通过常规继承使用int的方法。在空格式字符串的特殊处理情况下,将在对象上调用str(),而这对于bool已经实现了。 - UloPe
2
所以,为了既有非空格式字符串(例如用于对齐),又能将布尔值打印为true/false,我们应该先使用str(boolean)将其转换为字符串,然后将其作为字符串发送到格式化函数中? - dragonxlwang
显示剩余9条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接