String formatting with “{0:d}” doesn't convert to integer

python, string-formatting

Solution

A first approach to this problem might be simply to `try` it:

class MyIntegerTenClass:
    def __int__(self):
        return 10
    def __str__(self):
        return 'ten'
    def __format__(self, format_spec):
        try:
            s = format(str(self), format_spec)
        except ValueError:
            s = format(int(self), format_spec)
        return s

If `MyIntegerTenClass` can be inserted into the format string as a string, it will be. If not, it will be converted into an int and resubmitted to `format`.

>>> print '{0}, {0:s}, {0:d}, {0:02X}, {0:f}'.format(ten)
ten, ten, 10, 0A, 10.000000

If you want the default representation to be `10` instead of `ten`, you need only swap the conversion lines:

    def __format__(self, format_spec):
        try:
            s = format(int(self), format_spec)
        except ValueError:
            s = format(str(self), format_spec)
        return s

Test output:

>>> print '{0}, {0:s}, {0:d}, {0:02X}, {0:f}'.format(ten)
10, ten, 10, 0A, 10.000000

As an addendum, I don't believe you'll be able to get the behavior you want without defining `__format__`; however, you can develop a more sophisticated approach using a `Formatter` object. Still, I think the exception-based approach gives you a lot of built-in functionality for free.

Problem

This is about the same issue as in this question about floats. When you've got a value that could get converted to an integer, the old `%d` would convert it, but format doesn't. ``` class MyIntegerTenClass: def __int__(self): return 10 def __str__(self): return 'ten' ten = MyIntegerTenClass() print '%d, %02X, %s' % (ten, ten, ten) # ok print '{0}'.format(ten) # ok print '{0:d}, {0:02X}'.format(ten) # ValueError: Unknown format code 'd' for object of type 'str' ``` Is there a way to modify the behaviour of format, without touching the class of the value to be formatted (without adding a `__format__` method to that class)? Edit: My goal is to get the formatting dependent on the format string, but not on the value. So if the format string says "d" or "x", convert the value to int and then to decimal or hexadecimal representation. If the format string says "s", convert it to string directly. As the old `%` did. Actually, I could even add a `__format__` method to the class of the value. But how do I check, in that method, if the given format specification is an integer format specification? Without reimplementing the format specification parser of the builtin format. Edit: Here's a solution with `__format__` and exceptions. Any better ideas? ``` class MyIntegerTenClass: def __int__(self): return 10 def __str__(self): return 'ten' def __format__(self, spec): fmt = '{0:%s}'%spec try: return fmt.format(str(self)) except: return fmt.format(int(self)) ten = MyIntegerTenClass() print '%d, %02X, %s' % (ten, ten, ten) # ok, prints "10, 0A, ten" print '{0:d}, {0:02X}, {0}'.format(ten) # ok, prints "10, 0A, ten" ```

Original source

Related problems