[Python-Dev] Passing floats to "i" parser marker

Guido van Rossum guido@python.org
Wed, 05 Feb 2003 10:04:34 -0500


> > The problem (and my opinion) has come up here on and off many times.
> 
> Hmm, I must have been on vacation then :-)

Yeah, I miss those long European vacations... :-)

> > Since all the type knows is that it's __int__ method is called, not
> > that this is in the context of getargs.c, how could it protest in this
> > case without protesting about all calls to int()?
> 
> But that's the point: "i" should work just like int() does
> w/r to the __int__ slot and it does, so nothing is broken
> which would need fixing.

Well, that's where we disagree.  I think "i" is broken and needs
fixing.

The history:

Long ago, "i" and friends only accepted honest-to-god ints; if you had
something else that implemented __int__, you had to call int() on it
before passing it along.  list.insert() only accepted ints for the
index, and all was good.

Then someone suggested that it wasn't fair that built-in ints had
special status when passed to a function implemented in C, and that
anything that supported __int__ should be allowed.  The use case
(though we didn't have that word then yet) was some user-defined type
that represented an int with additional behavior (it was before you
could subclass int).

It wasn't until years after the idea was implemented that I realized
that floats also have an __int__ method and that you could write not
only "list.insert(3.0, xxx)", which was arguably acceptable, but also
"list.insert(3.14, xxx)", which was definitely not.

So the warning for "i" is an attempt at a fix for this that has the
largest bang for the buck (as opposed to introducing a second method
for lossy conversions to int, which would be much more work and cause
much more broken code).

> IMHO, the right way to fix what you think is broken is by introducing
> a new parser marker with your new semantics. Then use that parser
> marker whereever it is found meaningful.

Tell that to the people who have to maintain thousands of PyArg_*
calls.

I think that a parser marker meaning "accept a float but truncate it
to int" would not find much use.

> The problem is that your change propogates to all third-party
> extensions as well and that's not desirable in all cases. IMHO,
> it is not even desirable for all cases in the Python interpreter
> (see below).
> 
> > By far the majority of types that implement __int__ represent int-like
> > data; only floats and rationals use __int__ for losing information.
> > That's why the above solution was chosen.
> 
> I'm sure that all numeric-like types implement __int__ for
> the sake of being compatible to int().

Understood.  We're going for a 90% solution here (see above).

> > Why do you think so much will break?  In Python 2.2, you can write
> > 
> >   a = range(10)
> >   a.insert(3.14, 0)
> > 
> > and it will insert a 0 at position 3.  This is undocumented.  Do you
> > really think anyone in the world willfully uses this?
> 
> No, this is one spot where the new parser marker would fit
> well. Still, even in this case I think that passing
> floats to "i" is perfectly OK, namely in all those cases
> where no loss of data occurs, e.g. passing 13.0 to "i"
> should be accepted as 13.

Smarter people than you and me can disagree about that.  Personally, I
think that accepting floats with int values here is asking for
trouble: a calculation that returns a perfect int on one machine may
not do so on another, and hence a subtle portability problem is born.

> >>Which makes me think: int() started
> >>to return longs a few months ago -- we don't even have
> >>a cast which would raise an OverlfowError in case
> >>the conversion to int is not possible.
> > 
> > Why would you need one (except in C code, where "i" is such a cast)?
> 
> For exactly this case -- to make sure that "i" doesn't
> overflow when receiving the return value from int().

This makes no sense.  "i" gives a perfectly valid error message when
passed a value that's too long to fit in a C int -- in fact, the error
message is exactly the same as you used to get from int(x) if x were a
long larger than sys.maxint.

> > In the future, the difference between int and long will disappear
> > completely.

> >>Of course, int()
> >>would return a long and then the "i" parser marker
> >>implementation would complain, but that may be too late, e.g.
> >>in the case where you pass the data over the wire or
> >>store it in a file for later processing.
> > 
> > YAGNI.
> 
> I wasn't proposing anything in the above paragraph...
> what does YAGNI apply to here ?

I sohuld have invented a new acronym.  What I meant was YAWTM (You Are
Worrying Too Much).

--Guido van Rossum (home page: http://www.python.org/~guido/)