Open mwtoews opened 3 years ago
Hi, thanks for this suggestion.
TLDR: I'm open to having this in flynt, but unlikely to find time to implement it as of now. Unless it's provably safe, this should be under --aggressive flag or some new flag and not a default behavior.
The main concern of flynt
is to convert .format calls and % formatting to f-strings. As I understand, any code that has .format() call could together with e.g. ascii() was not using the possible formatting notation before.
So this is like an opportunity to solve one more problem, namely not using the !r, !s, !a formatting modifiers. I suspect that many programmers actually don't know about them at all.
flynt
iff under the hood exactly the same mechanisms are guaranteed to be called.!s
? I thought that everything without a specifier will be treated as !s; if it is so, maybe it should always be omitted?I'd imagine transferring simple str(x)
their equivalent !s
conversion field is safe, as this is the expected behaviour since Python 3.0 (and possibly Python 2.6, but this only had !r
and !s
). But yes, f{x!s}
is different than f{x}
.
The numpy demo above is not obvious, so here is a clearer example of the class behaviour:
class Foo:
def __format__(self, format_spec):
return f"format with spec: {format_spec!r}"
def __str__(self):
return "str"
def __repr__(self):
return "repr–"
obj = Foo()
print(f"{obj}")
print(f"{obj:some things}")
print(f"{obj!s}")
print(f"{obj!r}")
print(f"{obj!a}")
print(f"{obj!a:-^30}")
prints
format with spec: ''
format with spec: 'some things'
str
repr–
repr\u2013
----------repr\u2013----------
I'll admit that I thought !s
was the same as without too, but internally it calls either __str__
or __format__
methods, where implemented. And the content of format_spec
(after :
) is a whole other thing that has nothing to do with this feature.
The Format String Syntax includes a conversion field, which can be one of:
!s
to callstr()
!r
to callrepr()
!a
to callascii()
E.g. take a look at the following:
(side note about the "bad output" is that is due to conversion from float32 to Python's native 64-bit representation, and we never want to see this as it's a bad conversion).
Currently, flynt v.0.65 doesn't attempt to modify the conversion field of the format string. For example:
"{}".format(str(x))
(and"%s" % str(x)
) should be translated tof"{x!s}"
rather thanf"{str(x)}"
"{}".format(repr(x))
(and"%s" % repr(x)
) should be translated tof"{x!r}"
rather thanf"{repr(x)}"
."{}".format(ascii(x))
(and"%s" % ascii(x)
) should be translated tof"{x!a}"
rather thanf"{ascii(x)}"
.The motivation behind this suggestion is that the resulting conversion is shorter, preserves output intent (i.e. no unexpected conversions, demonstrated with numpy), and tests with
%timeit
consistently show measurable efficiencies with format strings that directly use the conversion field. This is shown with:As you can see, fewer processing steps are required when format conversions are embedded.