Casting to a "number" (both int and float)?

Jussi Piitulainen harvesting at makes.address.invalid
Fri Aug 28 07:46:11 CEST 2015


Victor Hooi writes:

> I'm reading JSON output from an input file, and extracting values.
>
> Many of the fields are meant to be numerical, however, some fields are
> wrapped in a "floatApprox" dict, which messed with my parsing.
>
> For example:
>
> {
>     "hostname": "example.com",
>     "version": "3.0.5",
>     "pid": {
>         "floatApprox": 18403
>     }
>     "network": {
>         "bytesIn": 123123,
>         "bytesOut": {
>             "floatApprox": 213123123
>         }
> }
>
> The floatApprox wrapping appears to happen sporadically in the input.
>
> I'd like to find a way to deal with this robustly.

So you want a value as is when it is a number - either an int or a
float. And otherwise the value is a dict and you want the value at a
known key. Here's a literal-minded expression of the spec:

   ( o if isinstance(o, (int, float)) else o['floatApprox'] )

> For example, I have the following function:
>
> def strip_floatApprox_wrapping(field):
>     # Extracts a integer value from a field. Workaround for the
>     # float_approx wrapping.
>     try:
>         return int(field)
>     except TypeError:
>         return int(field['floatApprox'])
>
> which I can then call on each field I want to extract.
>
> However, this relies on casting to int, which will only work for ints
> - for some fields, they may actually be floats, and I'd like to
> preserve that if possible.

You could try int first, float second; you should still return the
actual value.

   try:
     int(field)
     return field
   except TypeError: pass
   try:
     float(field)
     return field:
   except TypeError:
     return field['floatApprox']

There are a couple of reasons to return the actual value instead of the
result of the "cast". First, it appears to be what you want :) Second,
it allows you to have some confidence that the data actually is numeric
and not, say, strings that happen to look like numbers.

> (I know there's a isnumber() field - but you can only call that on a
> string - so if I do hit a floatApprox field, it will trigger a
> AttributeError exception, which seems a bit clunky to handle).
>
> def strip_floatApprox_wrapping(field):
>     # Extracts a integer value from a field. Workaround for the
>     # float_approx wrapping.
>     try:
>         if field.isnumeric():
>             return field
>     except AttributeError:
>         return field['floatApprox']

I take it you haven't tried this and the fields in question actually are
numeric, not strings. That exception would be taken for a wrong reason.

You could do the following, but str.isnumeric does not do what you want.
For example, "3.14".isnumeric() is as False as "{}".isnumeric().

    if str(field).isnumeric():
        return field
    else:
        return field['floatApprox']

> Is there a way to re-write strip_floatApprox_wrapping to handle both
> ints/floats, and preserve the original format?
>
> Or is there a more elegant way to deal with the arbitrary nesting with
> floatApprox?

Consider ... if isinstance(field, (int, float)) else ... .


More information about the Python-list mailing list