0

I am writing a custom encoding function for use with msgpack-python. I wish to convert any numpy.float object to float before letting msgpack-python serialise it. My encoding function looks like this:

def encode_custom(obj):        
    if issubclass(obj.__class__,np.float):
        obj = float(obj)
    return obj

which works just fine. However, the top voted answer at How do I check (at runtime) if one class is a subclass of another? suggests that this is a bad idea. I assume this is because this method doesn't use duck-typing.

Is there a way to duck-type the encoding function?

EDIT: Note that I only want float-like objects to convert to float. Objects that are better represented as another type (e.g. ints) should use that other type, even if they can be float()'d into a float object.

Community
  • 1
  • 1
Charlie
  • 469
  • 4
  • 15

3 Answers3

2

Special casing the type here is perfectly reasonable - because it sounds like you're feeding it into an API that doesn't work with duck-typing anyway.

But as mentioned elsewhere, np.float is float, so that's not going to work. You probably want either isinstance(val, np.floating), or isinstance(val, np.inexact).

For future reference, if you want to know what the entire class hierarchy (excluding abcs) is, you can get it with the .__mro__ property:

>>> np.float32.__mro__
(<class 'numpy.float32'>, <class 'numpy.floating'>, <class 'numpy.inexact'>, <class 'numpy.number'>, <class 'numpy.generic'>, <class 'object'>)
Eric
  • 95,302
  • 53
  • 242
  • 374
0

You could call one of the NumPy routines that convert NumPy scalars to Python scalars:

try:
    return obj.item()
except AttributeError:
    return obj

or

try:
    return obj.tolist() # yes, tolist. It won't return a list here.
except AttributeError:
    return obj

Note that it might be better to just use isinstance(obj, numpy.float64), or isinstance(obj, (numpy.float32, numpy.float64)). Duck typing makes sense for things like iterables, where you try to treat them as iterables, and if it works, you know they're iterables. Here? You try to treat your object like a numpy scalar, and if it works, you know... that the object has an item method, or a tolist method. That's not really the information you're interested in.

If you want to check whether an object has an actual, specific type, rather than checking what operations it provides, isinstance is generally the way to go.

user2357112
  • 260,549
  • 28
  • 431
  • 505
0

The number 1 answer to any python question is "it depends". With duck-typing you simply use the object assuming its correct and catch exceptions when everything goes wrong. In your case, that would be:

def encode_custom(obj):
    """Strict type checking: change to float, but raise ValueError
    on fail and let upper level deal with it."""
    return float(obj)

def encode_custom(obj):
    """Loose type checking: change to float or return unchanged"""
    try:
        return float(obj)
    except ValueError:
        return obj

This would work with the various floats and with everything that can be converted to a float. things like np.int16, np.bool_ and str (if it happens to represent an int or float). Now to the "it depends". This is great if you want all of these things to covert or lousy if your program defines these non-float things as garbage and you want to error out.

In that case, instead of checking subclass, check instance so you can handle any weird multiple inheritance or meta programming that is going on.

def encode_custom(obj):        
    if isinstance(obj,np.float):
        obj = float(obj)
    return obj
tdelaney
  • 73,364
  • 6
  • 83
  • 116
  • 2
    Note that `np.float` is just another name for the Python built-in `float`. More importantly, note that `isinstance(np.float32(1.0), float)` returns `False`. – Warren Weckesser Dec 02 '16 at 18:40
  • 1
    @WarrenWeckesser - Interesting. I checked `np.float64` and it worked. `issubclass` for `np.float32` doesn't work either. Hmmm.... – tdelaney Dec 02 '16 at 18:43