pythondowncast

How do I downcast in python


I have two classes - one which inherits from the other. I want to know how to cast to (or create a new variable of) the sub class. I have searched around a bit and mostly 'downcasting' like this seems to be frowned upon, and there are some slightly dodgy workarounds like setting instance.__class__ - though this doesn't seem like a nice way to go.

eg. http://www.gossamer-threads.com/lists/python/python/871571 http://code.activestate.com/lists/python-list/311043/

sub question - is downcasting really that bad? If so why?

I have simplified code example below - basically i have some code that creates a Peak object after having done some analysis of x, y data. outside this code I know that the data is 'PSD' data power spectral density - so it has some extra attributes. How do i down cast from Peak, to Psd_Peak?

"""Two classes"""
import numpy as np

class Peak(object):
    """Object for holding information about a peak"""
    def __init__(
        self,
        index,
        xlowerbound = None,
        xupperbound = None,
        xvalue= None,
        yvalue= None
    ):
        self.index = index # peak index is index of x and y value in psd_array

        self.xlowerbound = xlowerbound
        self.xupperbound = xupperbound

        self.xvalue  = xvalue
        self.yvalue  = yvalue


class Psd_Peak(Peak):
    """
    Object for holding information about a peak in psd spectrum

    Holds a few other values over and above the Peak object.
    """
    def __init__(
        self,
        index,
        xlowerbound=None,
        xupperbound=None,
        xvalue=None,
        yvalue=None,
        depth=None,
        ampest=None
    ):
        super(Psd_Peak, self).__init__(
            index,
            xlowerbound,
            xupperbound,
            xvalue,
            yvalue)

        self.depth = depth
        self.ampest = ampest

        self.depthresidual = None
        self.depthrsquared = None


def peakfind(xdata,ydata) :
    '''Does some stuff.... returns a peak.'''
    return Peak(1, 0, 1, .5, 10)

# Find a peak in the data.
p = peakfind(np.random.rand(10),np.random.rand(10))

# Actually the data i used was PSD -
#  so I want to add some more values tot he object

p_psd = ????????????

edit

Thanks for the contributions.... I'm afraid I was feeling rather downcast(geddit?) since the answers thus far seem to suggest I spend time hard coding converters from one class type to another. I have come up with a more automatic way of doing this - basically looping through the attributes of the class and transfering them one to another. how does this smell to people - is it a reasonable thing to do - or does it spell trouble ahead?

def downcast_convert(ancestor, descendent):
    """
    automatic downcast conversion.....

    (NOTE - not type-safe -
    if ancestor isn't a super class of descendent, it may well break)
    """
    for name, value in vars(ancestor).iteritems():
        #print "setting descendent", name, ": ", value, "ancestor", name
        setattr(descendent, name, value)
    return descendent

Solution

  • You don't actually "cast" objects in Python. Instead you generally convert them -- take the old object, create a new one, throw the old one away. For this to work, the class of the new object must be designed to take an instance of the old object in its __init__ method and do the appropriate thing (sometimes, if a class can accept more than one kind of object when creating it, it will have alternate constructors for that purpose).

    You can indeed change the class of an instance by pointing its __class__ attribute to a different class, but that class may not work properly with the instance. Furthermore, this practice is IMHO a "smell" indicating that you should probably be taking a different approach.

    In practice, you almost never need to worry about types in Python. (With obvious exceptions: for example, trying to add two objects. Even in such cases, the checks are as broad as possible; here, Python would check for a numeric type, or a type that can be converted to a number, rather than a specific type.) Thus it rarely matters what the actual class of an object is, as long as it has the attributes and methods that whatever code is using it needs.