[Python-3000] More PEP 3101 changes incoming

Ron Adam rrr at ronadam.com
Thu Aug 2 06:58:40 CEST 2007



Talin wrote:
> I had a long discussion with Guido today, where he pointed out numerous 
> flaws and inconsistencies in my PEP that I had overlooked. I won't go 
> into all of the details of what he found, but I'd like to focus on what 
> came out of the discussion. I'm going to be updating the PEP to 
> incorporate the latest thinking, but I thought I would post it on Py3K 
> first to see what people think.
> 
> The first change is to divide the conversion specifiers into two parts, 
> which we will call "alignment specifiers" and "format specifiers". So 
> the new syntax for a format field will be:
> 
>      valueSpec [,alignmentSpec] [:formatSpec]
> 
> In other words, alignmentSpec is introduced by a comma, and conversion 
> spec is introduced by a colon. This use of comma and colon is taken 
> directly from .Net. although our alignment and conversion specifiers 
> themselves look nothing like the ones in .Net.
> 
> Alignment specifiers now includes the former 'fill', 'align' and 'width' 
> properties. So for example, to indicate a field width of 8:
> 
>      "Property count {0,8}".format(propertyCount)

How would I specify align right, and width 42?


> The 'formatSpec' now includes the former 'sign' and 'type' parameters:
> 
>      "Number of errors: {0:+d}".format(errCount)
 >
> In the preceding example, this would indicate an integer field preceded 
> by a sign for both positive and negative numbers.

How can I get negative numbers to print in red?  Just kidding. ;-)

(I recently was frustrated by not being able to have text of two different 
colors in a single tkinter button.)


> There are still some things to be worked out. For example, there are 
> currently 3 different meanings of 'width': Minimum width, maximum width, 
> and number of digits of decimal precision. The previous version of the 
> PEP followed the 2.x convention, which was 'n.n' - 'min.prec' for 
> floats, and 'min.max' for everything else. However, that seems confusing.

Yep, enough so that I need to look it up more often than I like.

> (I'm actually still working out the details - and in fact a little bit 
> of a bikeshed discussion would be welcome at this point, as I could use 
> some help ironing out these kinds of little inconsistencies.)
> 
> In general, you can think of the difference between format specifier and 
> alignment specifier as:
> 
>      Format Specifier: Controls how the value is converted to a string.
>      Alignment Specifier: Controls how the string is placed on the line.

Keeping related terms together is important I think.  The order]

    {item, alignment: format}

Splits the item and it's formatter.  The alignment is more of a container 
property or feild property as you pointed out further down.

So maybe if you group the related values together...

     {valuespec:format, alignment:width}

This has a nice dictionary feel and maybe that may be useful as well. 
Reusing things I'm familiar with does make it easier.


So it would be possible to create a dictionary and use it's repr() function 
as a formatter.  Nice little bonus. ;-)

     string_format = {0:'i', 'R':12}
     string_format.repr().format(number)

Well almost, there is the tiny problem of the quotes inside it.  :/


So in order to right justify an integer...

    "Property count {0:i, R:8}".format(propertyCount)


The precision for floats is not part of the field width, so it belongs in 
formatter term.

    "Total cost {0:f2, R:12}".format(totalcost)

I'm not sure if it should be 'f.2' or just "f2".


> Another change in the behavior is that the __format__ special method can 
> only be used to override the format specifier - it can't be used to 
> override the alignment specifier. The reason is simple: __format__ is 
> used to control how your object is string-ified. It shouldn't get 
> involved in things like left/right alignment or field width, which are 
> really properties of the field, not the object being printed.

Right, so maybe there should be both a __format__, and an __alignment__ method?


> The __format__ special method can basically completely change how the 
> format specifier is interpreted. So for example for Date objects you can 
> have a format specifier that looks like the input to strftime().
> 
> However, there are times when you want to override the __format__ hook. 
> The primary use case is the 'r' conversion specifier, which is used to 
> get the repr() of an object.
> 
> At the moment I'm leaning towards using the exclamation mark ('!') to 
> indicate this, in a way that's analogous to the CSS "! important" flag - 
> it basically means "No, I really mean it!" Two possible syntax 
> alternatives are:
> 
>      "The repr is {0!r}".format(obj)
>      "The repr is {0:r!}".format(obj)
> 
> In the first option, we use '!' in place of the colon. In the second 
> case, we use '!' as a suffix.

-1

This doesn't feel right to me.  It seems to me if you do this, then we will 
see all sorts of weird __repr__ methods that return things completely 
different than we get now.


As an alternative ...

Some sort of indirect formatting should be possible, but maybe it can be 
part of the data passed to the format method.

     "{0:s, L:10} = {1:!, L:12}".format(obj.name, (obj, rept))


Another possibility is to chain them some how.

     "{0:!, L:10} = {1:!, L:12}".formatfn(str, repr).format(obj, obj)

Same could be done with the alignments if there is a use case for it.


> Another change suggested by Guido is explicit support for the Decimal 
> type. Under the current proposal, a format specifier of 'f' will cause 
> the Decimal object to be coerced to float before printing. That's not 
> what we want, because it will cause a loss of precision. Instead, the 
> rule should be that Decimal can use all of the same formatting types as 
> float, but it won't try to convert the Decimal to float as an 
> intermediate step.

+1

I'm hoping at some point (in the not too far future) I will be able to tell 
python to use Decimal in place of floats and not have to change nearly 
every function that produces a float literal.  This is a step in that 
direction.

> Here's some pseudo code outlining how the new formatting algorithm for 
> fields will work:
> 
>      def format_field(value, alignmentSpec, formatSpec):
>          if value has a __format__ attribute, and no '!' flag:
>              s = value.__format__(value, formatSpec)
>          else:
>              if the formatSpec is 'r':
>                   s = repr(value)
>              else if the formatSpec is 'd' or one of the integer types:
>                   # Coerce to int
>                   s = formatInteger(int(value), formatSpec)
>              else if the formatSpec is 'f' or one of the float types:
>                    if value is a Decimal:
>                        s = formatDecimal(value, formatSpec)
>                    else:
>                        # Coerce to float
>                        s = formatFloat(float(value), formatSpec)
>              else:
>                  s = str(value)
> 
>      # Now that we have 's', apply the alignment options
>      return applyAlignment(s, alignmentSpec)
> 
> My goal is that some time in the next several weeks I would like to get 
> working a C implementation of just this function. Most of the complexity 
> of the PEP implementation is right here IMHO.
> 
> Before I edit the PEP I'm going to let this marinate for a week and see 
> what the discussion brings up.
> 
> -- Talin

Great work Talin, I cheer your efforts at keeping this moving given the 
many directions and turns it has taken!

Cheers,
    Ron



More information about the Python-3000 mailing list