22

I was trying to print a truth table for Boolean expressions. While doing this, I stumbled upon the following:

>>> 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'

As soon as I specify a format specifier, format() converts True to 1. I know that bool is a subclass of int, so that True evaluates to 1:

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

But why does using the format specifier change 'True' to 1 in the first example?

I turned to the docs for clarification. The only thing it says is:

A general convention is that an empty format string ("") produces the same result as if you had called str() on the value. A non-empty format string typically modifies the result.

So the string gets modified when you use a format specifier. But why the change from True to 1 if only an alignment operator (e.g. ^) is specified?

5
  • 1
    "A general convention is that an empty format string ("") produces the same result as if you had called str() on the value. A non-empty format string typically modifies the result." -- docs Commented May 14, 2014 at 12:41
  • I don't know why it's done like that, but if you want to fix it you could do format(str(True),"^") Commented May 14, 2014 at 12:47
  • Thanks, I already fixed it, but I was just curious as to the "why" :) Commented May 14, 2014 at 12:47
  • My guess is it has to do with an implicit conversion for the '^' operator, although that is weird. Also note that '^' is the align-center operator, not the width operator. Commented May 14, 2014 at 23:47
  • Yes, it is weird. Corrected the question. Commented May 15, 2014 at 7:29

1 Answer 1

9

Excellent question! I believe I have the answer. This requires digging around through the Python source code in C, so bear with me.

First, format(obj, format_spec) is just syntactic sugar for obj.__format__(format_spec). For specifically where this occurs, you'd have to look in abstract.c, in the function:

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);

    ...
}

To find the exact call, we have to look in 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 is actually defined as a macro in formatter_string.c as a function found in 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
}

And therein lies your answer. A simple check for whether format_spec_len is 0, and if it is, convert obj into a string. As you well know, str(True) is 'True', and the mystery is over!

Sign up to request clarification or add additional context in comments.

14 Comments

I think the OP is asking why '^'.format(True) == '1', not why ''.format(True) == 'True'. Or rather why there would be an implicit cast to int for the former when the latter properly evaluates.
True is an int, and it's only cast out of an int and into a str when format_spec is empty.
Not in python it isn't. >>> type(True) = <type 'bool'>, and type(True) == type(1) == False. In the underlying C code in the C interpreter it obviously is converted into an int since C lacks a bool primitive, but that wouldn't explain the implicit conversion on the python side.
To clarify: Since bool is a subclass of int but bool doesn't specify it's own __format__ method the one from int is used through regular inheritance. In the case of the special handling of an empty format string, str() is called on the object which is implemented for bool.
So, in order to have both non-empty format string (such as for alignment) and boolean printed as true/false, we should first str(boolean) and send this (as a string) to format function?
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.