0

string formatting supports implicit strifying. However, string format specifiers don't appear to be compatible with this, only explicit str objects (regardless of using type=s). I cannot find this behavior documented anywhere without pulling up the cpython code. default ubuntu 20.04 python3.8 install. using an explicit str() call is workable but it's not very clean especially in my use case where it now liters the code.

obj = object()
explicit = str(obj)
print(f"{explicit:.5}")
print(f"{obj}")
print(f"{obj:.5}") # TypeError: unsupported format string passed to object.__format__

Question:

  • Have I interpreted this correctly
  • where should I be looking to glean this info myself (if exists outside cpython code), and
  • is there any way accomplish format specifiers {:...} while relying on the implicit str()ification?

3 Answers 3

3

You could either implement the __format__ method in your object class or place the str() directly in the format string:

print(f"{str(obj):.5}")

see PEP 498 for details.

note that f"{obj!s:.5}" also works but, in that same specification, !s and !r are considered redundant and only maintained for backward compatibility

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

3 Comments

yes, that's cleaner than how I wrote the example; regarding the rest of my question - is the difference between implicit/explicit string type documented anywhere outside the low-level code, or more generally the spec on coercion of any types in formatting (such as int->float)
You can find the info in PEP498: The actual code uses the equivalent of type(value).__format__(value, format_spec), or format(value, format_spec) which implies that the __format__() method of the result's type is used directly, hence the incompatible format on a base object()
As far as I can tell, there isn't a type coercion happening. int and float each have their own implementation of the __format__() method. At best, inheritance of derived types may be used.
1

It works if you explicitly request string conversion:

>>> print(f"{obj!s:.5}")
<obje

Behind the scenes, I suspect that the result of the expression is not passed to str first, but rather to format:

>>> format(obj, "%s")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported format string passed to object.__format__

In this case, object.__format__ itself doesn't automatically do a conversion to string using object.__str__, for whatever reason.

1 Comment

upvoted, thanks except for a ref to the docs this is correct - i glossed over a critical part of the error message, as it was already telling me it's objects __format__ that is called, no format() use of str directly, as you point out. I accepted the earlier answer but thank you for your help!
1

From the C source

/* Issue 7994: If we're converting to a string, we
   should reject format specifications */

Issue object.format should reject format strings argues that the base object from which all other objects are formed should not assume that it is a string-like thing that supports string specifiers. Format specifiers are type specific so the parent of all types can't make any judgement about them. It would have to cast itself to one of its subtypes which does have a formatter, but which one?

An interesting example is a user defined class that doesn't implement a formatter and so uses the default.

>>> class Foo:
...     pass
... 
>>> foo = Foo()
>>> f"{foo:.5}"
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported format string passed to Foo.__format__

To get your desired result, object.__format__ would have to call foo.__str__().__format__(the_specifier) but that's an odd thing to do. Why should it assume that string formatters are the right thing to apply here? Why not __repr__ instead? The object was clear that it don't want no stinking format spec, so don't give it one.

The solution is to provide an explicit string conversion first

f"{obj!s:.5}"

Comments

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.