8

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"
2
  • 1
    It says ten is a string, you are trying to format it as a number. Commented Jul 12, 2012 at 18:42
  • I'd just use !s in the format specification, to force the conversion to a string before applying the field parameters: '{0:d}, {0:02X}, {0!s}'.format(ten). Also, use return format(int(self), spec) no need to create a new string template here when there is a perfectly good format() function. Commented Sep 13, 2020 at 12:01

3 Answers 3

2

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.

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

1 Comment

Thanks. Seems we got the same solution at about the same time (see my second edit). Though your solution seems a bit nicer since it doesn't need that '{0:%s}'%spec. I didn't realize there's a format function you can call like this.
1

pass an integer instead of a string and it'll work fine.

>>> print  '{0:d}'.format(1)
1
>>> print  '{0:d}'.format('1')

Traceback (most recent call last):
  File "<pyshell#57>", line 1, in <module>
    print  '{0:d}'.format('1')
ValueError: Unknown format code 'd' for object of type 'str'

1 Comment

I knew that. But I don't want to pass an integer. I want to pass an object of my class which can be converted to an integer.
0

It says ten is a string, you are trying to format it as a number.

Try wrapping it in a cast:

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

1 Comment

Doesn't work for my use-case. I don't want to change the value given to format.

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.