[Python-Dev] PEP 461 - Adding % and {} formatting to bytes

Steven D'Aprano steve at pearwood.info
Fri Jan 17 08:47:37 CET 2014


On Thu, Jan 16, 2014 at 08:23:13AM -0800, Ethan Furman wrote:

> As I understand it, str.format will call the object's __format__.  So, for 
> example, if I say:
> 
>   u'the value is: %d' % myNum(17)
> 
> then it will be myNum.__format__ that gets called, not int.__format__; 

I seem to have missed something, because I am completely confused... Why 
are you talking about str.format and then show an example using % instead?

%d calls __str__, not __format__. This is in Python 3.3:

py> class MyNum(int):
...     def __str__(self):
...             print("Calling MyNum.__str__")
...             return super().__str__()
...     def __format__(self):
...             print("Calling MyNum.__format__")
...             return super().__format__()
...
py> n = MyNum(17)
py> u"%d" % n
Calling MyNum.__str__
'17'


By analogy, if we have a bytes %d formatting, surely it should either:

(1) call type(n).__bytes__(n), which is guaranteed to raise if the 
    result isn't ASCII (i.e. like len() raises if the result isn't 
    an int); or

(2) call type(n).__str__(n).encode("ascii", "strict").


Personally, I lean towards (2), even though that means you can't have a 
single class provide an ASCII string to b'%d' and a non-ASCII string to 
u'%d'. 


> this 
> is precisely what we don't want, since can't know that myNum is only going 
> to return ASCII characters.

It seems to me that Consenting Adults applies here. If class MyNum 
returns a non-ASCII string, then you ought to get a runtime exception, 
exactly the same as happens with just about every other failure in 
Python. If you don't want that possible exception, then don't use MyNum, 
or explicitly wrap it in a call to int:

b'the value is: %d' % int(MyNum(17))

The *worst* solution would be to completely ignore MyNum.__str__. 
That's a nasty violation of the Principle Of Least Surprise, and will 
lead to confusion ("why isn't my class' __str__ method being called?") 
and bugs.

* Explicit is better than implicit -- better to explicitly 
  wrap MyNum in a call to int() than to have bytes %d 
  automagically do it for you;

* Special cases aren't special enough to break the rules -- 
  bytes %d isn't so special that standard Python rules about
  calling special methods should be ignored;

* Errors should never pass silently -- if MyNum does the wrong 
  thing when used with bytes %d, you should get an exception.


> This is why I would have bytes.__format__, as part of its parsing, call 
> int, index, or float depending on the format code; so the above example 
> would have bytes.__format__ calling int() on myNum(17), 

The above example you give doesn't have any bytes in it. Can you explain 
what you meant to say? I'm guessing you intended this:

b'the value is: %d' % MyNum(17)

rather than using u'' as actually given, but I don't really know.



-- 
Steven


More information about the Python-Dev mailing list