11

我有两个类 - 一个继承自另一个。我想知道如何转换(或创建一个新变量)子类。我已经搜索了一下,而且大多数情况下像这样的“垂头丧气”似乎是不受欢迎的,并且有一些稍微狡猾的解决方法,比如设置实例。- 虽然这似乎不是一个好方法。

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

子问题 - 垂头丧气真的那么糟糕吗?如果是,为什么?

我在下面简化了代码示例 - 基本上我有一些代码在对 x、y 数据进行了一些分析后创建了一个 Peak 对象。在这段代码之外,我知道数据是“PSD”数据功率谱密度——所以它有一些额外的属性。我如何从 Peak 向下投射到 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 = ????????????

编辑

感谢您的贡献....恐怕我感到相当沮丧(geddit?),因为到目前为止的答案似乎表明我花时间从一种类类型到另一种类类型的硬编码转换器。我想出了一种更自动化的方法——基本上循环遍历类的属性并将它们相互转移。这种气味对人们有什么影响——这样做是否合理——或者它会给未来带来麻烦?

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
4

3 回答 3

10

您实际上并没有在 Python 中“投射”对象。相反,您通常会转换它们——取出旧对象,创建一个新对象,然后扔掉旧对象。为此,新对象的类必须设计为在其__init__方法中采用旧对象的实例并做适当的事情(有时,如果一个类在创建它时可以接受多种对象,它会为此目的有备用构造函数)。

您确实可以通过将实例的__class__属性指向不同的类来更改实例的类,但该类可能无法与实例一起正常工作。此外,恕我直言,这种做法是一种“气味”,表明您可能应该采取不同的方法。

在实践中,您几乎不需要担心 Python 中的类型。(有明显的例外:例如,尝试添加两个对象。即使在这种情况下,检查也尽可能广泛;在这里,Python 将检查数字类型或可以转换为数字的类型,而不是特定类型。)因此,对象的实际类是什么并不重要,只要它具有使用它的任何代码所需的属性和方法。

于 2013-03-03T16:19:00.153 回答
2

请参见以下示例。另外,一定要遵守 LSP(Liskov 替换原则)

class ToBeCastedObj:
    def __init__(self, *args, **kwargs):
        pass  # whatever you want to state

    # original methods
    # ...


class CastedObj(ToBeCastedObj):
    def __init__(self, *args, **kwargs):
        pass  # whatever you want to state

    @classmethod
    def cast(cls, to_be_casted_obj):
        casted_obj = cls()
        casted_obj.__dict__ = to_be_casted_obj.__dict__
        return casted_obj

    # new methods you want to add
    # ...
于 2018-12-10T02:39:28.070 回答
1

这不是一个向下转换的问题(恕我直言)。peekfind() 创建一个 Peak 对象——它不能被向下转换,因为它不是 Psd_Peak 对象——然后你想从中创建一个 Psd_Peak 对象。在 C++ 之类的东西中,您可能会依赖默认的复制构造函数——但这也行不通,即使在 C++ 中也是如此,因为您的 Psd_Peak 类在其构造函数中需要更多参数。在任何情况下,python 都没有复制构造函数,所以你最终会得到相当冗长的 (fred=fred, jane=jane) 东西。

一个好的解决方案可能是创建一个对象工厂并将您想要的 Peak 对象类型传递给 peekfind() 并让它为您创建正确的对象。

def peak_factory(peak_type, index, *args, **kw):
    """Create Peak objects

    peak_type     Type of peak object wanted
       (you could list types)
    index         index
       (you could list params for the various types)
    """
    # optionally sanity check parameters here
    # create object of desired type and return
    return peak_type(index, *args, **kw)

def peakfind(peak_type, xdata, ydata, **kw) :
    # do some stuff...
    return peak_factory(peak_type, 
         1,
         0,
         1,
         .5,
         10,
         **kw)

# Find a peak in the data.
p = peakfind(Psd_Peak, np.random.rand(10), np.random.rand(10), depth=111, ampest=222)
于 2013-03-03T19:58:16.067 回答