[Tutor] Limitation of int() in converting strings

Oscar Benjamin oscar.j.benjamin at gmail.com
Thu Dec 27 18:13:44 CET 2012


On 24 December 2012 04:42, eryksun <eryksun at gmail.com> wrote:
> On Sat, Dec 22, 2012 at 12:57 PM, Oscar Benjamin
> <oscar.j.benjamin at gmail.com> wrote:
>>>>
>>>> def make_int(obj):
>>>>      '''Coerce str, float and int to int without rounding error
>>>>      Accepts strings like '4.0' but not '4.1'
>>>>      '''
>>>>      fnum = float('%s' % obj)
>>>>      inum = int(fnum)
>>>>      assert inum == fnum
>>>>      return inum
>>>
>>> Well, that function is dangerously wrong. In no particular order,
>>> I can find four bugs and one design flaw.
>>
>> I expected someone to object to this function. I had hoped that they
>> might also offer an improved version, though. I can't see a good way
>> to do this without special casing the treatment of some or other type
>> (the obvious one being str).
>
> Strings don't implement __int__ or __trunc__; they aren't numbers, so
> why not special case them?

I hadn't realised that. Does the int(obj) function use isinstance(obj,
str) under the hood?

> You can parse strings with obj =
> Decimal(obj) (this uses a regex). Then for all inputs set inum =
> int(obj) and raise ValueError if inum != obj.

It had occurred to me that this would be the obvious fix for the issue
with large numbers. The result is:

from decimal import Decimal

def int_decimal(x):
    if isinstance(x, str):
        x = Decimal(x)
    ix = int(x)
    if ix != x:
        raise ValueError('Not an integer: %s' % x)
    return ix

Probably what I more often want, though, is a function that simply
refuses to handle real-valued types as inputs. That way if a float
sneaks in I can choose the appropriate rounding function (or bug fix)
at the source of the number. I'm not sure what's the best way to
detect real-valued types. At least for the stdlib using the numbers
module works:

from numbers import Integral

def int_(x):
    if not isinstance(x, (Integral, str)):
        raise TypeError('Need Integral: use round() or trunc()')
    return int(x)


Oscar


More information about the Tutor mailing list