[Python-3000] More PEP 3101 changes incoming

Carl Johnson carlmj at hawaii.edu
Mon Aug 13 12:08:50 CEST 2007


(First, let me apologize for diving into a bike shed discussion.)

There are two proposed ways to handle custom __format__ methods:

> class MyInt:
>      def __format__(self, spec):
>          if int.is_int_specifier(spec):
>              return int(self).__format__(spec)
>          return "MyInt instance with custom specifier " + spec
>      def __int__(self):
>          return <some local state>

and

> class MyInt:
>     def __format__(self, spec):
>         if is_custom_spec(spec):
>             return "MyInt instance with custom specifier " + spec
>         return NotImplemented
>     def __int__(self):
>         return <some local state>

I think this would be more straightforward as:

class MyInt:
     def __format__(self, spec):
         if is_MyInt_specific_spec(spec):
             return "MyInt instance with custom specifier " + spec
	else:
             return int(self).__format__(spec)
     def __int__(self):
         return <some local state>

The makers of the MyInt class should be the ones responsible for  
knowing that
MyInt can be converted to int as needed for output. If they want  
MyInt to
handle all the same format spec options as MyInt, it's up to them to  
either
implement them all in their __format__ or to cast the instance object  
to int
then call its __format__ object by themselves. I don't see the point  
in having
format guess what MyInt should be converted to if it can't handle the  
options
passed to it. If we go too far down this road, if MyInt craps out  
when given
":MM-DD-YY", then format will be obliged to try casting to Date just  
to see if
it will work. No, I think the format function should be somewhat  
dumb, since
dumb makes more sense to __format__ implementers than clever. Let  
them figure
out what their type can be cast into.

In the case that regular int can't handle the given format spec either,
int.__format__ will raise (return?) NotImplemented, in which case the  
format
function will try string conversion, and then if that also pukes, a  
runtime
exception should be raised.

I also like the idea of using "!r" for calling repr and agree that it  
should be
listed first. The syntax seems to be calling out for a little bit of  
extension
though. Might it be nice to be able to do something like this?

s = "10"
print("{0!i:+d}".format(s)) #prints "+10"

The !i attempts to cast the string to int. If it fails, then an  
exception is
raised. If it succeeds, then the int.__format__ method is used on the  
remainder
of the spec string. The logic is that ! commands are abbreviated  
functions that
are applied to the input before other formatting options are given.

On the one hand, this does risk a descent into "line noise" if too  
many !
options are provided. On the other hand, I think that providing !  
options for
just repr, str, int, and float probably wouldn't be too bad, and  
might save
some tedious writing of int(s), etc. in spots. It seems like if we're  
going to
have a weird syntax for repr anyway, we might as well use it to make  
things
more convenient in other ways. Or is this too TMTOWTDI-ish, since one  
could
just write int(s) instead? (But by that logic, one could write repr 
(s) too…)

The format function would end up looking like this:

def format(obj, spec):
     if spec[0] == "!":
         switch statement for applying obj = repr(obj), obj = int 
(obj), etc.
         spec = spec[2:]
     if obj.__format__ and type(obj) is not str:
         try:
             #if spec contains letters not understood, __format__  
raises NI
             return obj.__format__(spec)
         except NotImplemented:
             pass #everything gets put through str as a last resort
     return str(obj).__format__(spec) #last chance before throwing  
exception

Does this make sense to anyone else?

--Carl Johnson


More information about the Python-3000 mailing list