简体   繁体   English

我如何在python中向下转换

[英]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/ 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. 我在下面简化了代码示例 - 基本上我有一些代码在对x,y数据进行一些分析后创建了一个Peak对象。 outside this code I know that the data is 'PSD' data power spectral density - so it has some extra attributes. 在这段代码之外我知道数据是'PSD'数据功率谱密度 - 所以它有一些额外的属性。 How do i down cast from Peak, to 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 = ????????????

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. 感谢您的贡献....我担心我感觉相当沮丧(geddit?),因为到目前为止的答案似乎表明我花时间将转换器从一种类型编码到另一种类型。 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

You don't actually "cast" objects in Python. 你实际上并没有在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). 为了实现这一点,新对象的类必须设计为在其__init__方法中获取旧对象的实例并执行适当的操作(有时,如果类在创建它时可以接受多种对象,则它为此目的将有替代构造函数)。

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. 您确实可以通过将其__class__属性指向其他类来更改实例的类,但该类可能无法与实例一起正常工作。 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. 在实践中,您几乎不需要担心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. (有明显的例外:例如,尝试添加两个对象。即使在这种情况下,检查也尽可能广泛;这里,Python会检查数字类型,或者可以转换为数字的类型,而不是一个特定的类型。)因此,对象的实际类是很重要的,只要它具有任何代码使用它所需的属性和方法。

This isn't a downcasting problem (IMHO). 这不是一个向下倾斜的问题(恕我直言)。 peekfind() creates a Peak object - it can't be downcast because its not a Psd_Peak object - and later you want to create a Psd_Peak object from it. peekfind()创建一个Peak对象 - 它不能向下转换,因为它不是Psd_Peak对象 - 后来你想从它创建一个Psd_Peak对象。 In something like C++, you'd likely rely on the default copy constructor - but that's not going to work, even in C++, because your Psd_Peak class requires more parameters in its constructor. 在像C ++这样的东西中,您可能依赖于默认的复制构造函数 - 但即使在C ++中也不行,因为您的Psd_Peak类在其构造函数中需要更多参数。 In any case, python doesn't have a copy constructor, so you end up with the rather verbose (fred=fred, jane=jane) stuff. 在任何情况下,python都没有复制构造函数,所以你最终会得到相当冗长(fred = fred,jane = jane)的东西。

A good solution may be to create an object factory and pass the type of Peak object you want to peekfind() and let it create the right one for you. 一个好的解决方案可能是创建一个对象工厂并传递你想要peekfind()的Peak对象类型,并让它为你创建一个正确的对象。

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)

See following example. 请参阅以下示例。 Also, be sure to obey the LSP (Liskov Substitution Principle) 另外, 一定要遵守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
    # ...

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM