在python中创建包装类时,如何获取超类';s方法返回包装器类的实例

在python中创建包装类时,如何获取超类';s方法返回包装器类的实例,python,inheritance,pandas,Python,Inheritance,Pandas,例如,如果我在数据帧周围创建一个简单的包装器: from pandas import DataFrame class MyDataFrame(DataFrame): def __init__(self,data): DataFrame.__init__(self,data) @staticmethod def add(a, b): return a + b 然后我将其实例化

例如,如果我在数据帧周围创建一个简单的包装器:

    from pandas import DataFrame

    class MyDataFrame(DataFrame):

        def __init__(self,data):

            DataFrame.__init__(self,data)

        @staticmethod
        def add(a, b):
            return a + b
然后我将其实例化

    x = MyDataFrame([1,2,3])

            x.add(1,2)
            #    => 3

            type(x)
            #    => __main__.MyDataFrame
它起作用了。但是,如果我对返回dataframe的dataframe方法进行调用,它就不是我的包装器类的实例

    y = x.reindex([3,4,5])
    type(y)
    #    => pandas.core.frame.DataFrame

如何让它为所有DataFrame方法返回MyDataFrame实例?这是一个普遍的问题吗?我是否以错误的方式处理了这个问题?

您展示的示例不是包装器,而是Python中的一个子类。现在,在您的例子中,python子类和方法解析是按照简单的规则进行的

  • 查看方法的接收方对象的
    类型
    
  • 检查该类的类层次结构,并找到该方法定义的第一个实例。然后查看该方法的签名并相应地执行它。在您的例子中,类层次结构是简单的子类超类 所以,在你的情况下

  • x
    被定义为类的对象
    MyDataFrame
    ——simple。显然,根据定义,
    type(x)
    MyDataFrame
  • 在调用
    add
    的过程中,它查看receiver对象,它是
    MyDataFrame
    类的
    x
    。这个类实际上定义了方法
    add
    。因此,它只返回该方法的结果,奇怪的是,尝试调用
    DataFrame([1,2,3])。添加(1,2)
    。结果将不同,因为它查看
    add
    方法,如
    pandas.DataFrame
    类中定义的
  • 现在是第三部分——让我们应用同样的推理<代码>重新索引
  • 未在
    MyDataFrame
    中定义。下一步我们应该去哪里?类层次结构,这意味着
    pandas.DataFrame
    。现在,
    reindex
    确实是由这个类定义的,它返回一个pandas.DataFrame对象。(见此:)难怪
    y
    pandas数据帧
  • 现在我不明白您首先扩展熊猫数据帧是在做什么。像这样扩展不是常见的做法。如果你提供你想要做的细节,也许我们可以提供一个解决方案


    编辑:您最初的问题与扩展方法或扩展对象有关(C#有它们,正如您正确指出的,JS原型为您提供了相同的功能。Python没有扩展方法/对象作为第一类成员。对此进行了讨论。例如)

    在Pandas中,有几种情况下类没有很好地实现,无法形成派生类的基础。其中一些问题是固定的,例如,和

    可以实现一个parent
    reindex
    方法,从而生成一个子类:

    from pandas import DataFrame
    
    class DF():
        def __init__(self, data):
            print('DF __init__')
            self.data = data
        def reindex(self, index):
            print('DF reindex')
            return self.__class__(self.data)
            # return DF(self.data)  # not like this!
    
    class MyDF(DF):
        def __init__(self, data):
            DF.__init__(self, data)
        @staticmethod
        def add(a, b):
            return a + b
    
    
    x = MyDF([1,2,3])
    
    x.add(1,2)
    #    => 3
    type(x)
    
    y = x.reindex([3,4,5])
    type(y)
    
    z = DF([1,2,3])
    type(z.reindex([1, 2]))
    
    在较新版本的Pandas中,“u构造函数”是在内部设置的,用于控制返回的类型。设置此class属性似乎可以实现以下目的:

    class MyDataFrame(DataFrame):
        def __init__(self, *args, **kwargs):
            DataFrame.__init__(self, *args, **kwargs)
        @staticmethod
        def add(a, b):
            return a + b
    
    MyDataFrame._constructor = MyDataFrame
    
    >>> type(y)
    <class '__main__.MyDataFrame'>
    
    类MyDataFrame(DataFrame):
    定义初始化(self,*args,**kwargs):
    数据帧.uuuu初始化(self,*args,**kwargs)
    @静力学方法
    def添加(a、b):
    返回a+b
    MyDataFrame.\u构造函数=MyDataFrame
    >>>类型(y)
    
    我认为这是正常的行为。除非被覆盖,否则这些方法只指向获取它的位置。节省内存。基本上,你不能。它需要来自超类的合作。超类的编写方式必须能够返回“我是其实例的任何类”的实例,但许多类(如DataFrame)都是硬编码的,只能返回该特定类的实例。您可以覆盖DataFrame类中的
    构造函数
    属性,但通常对DataFrame进行子分类是没有用的,试试合成。@Jeff:合成其实并不容易,因为你仍然需要重写每一个神奇的方法,使你的对象看起来像一个数据帧。问题是熊猫被硬编码为只使用它自己的类型,而不是参数化的。@BrenBarn从0.13开始就没有;可以很容易地子类化,只需像我说的那样重写构造函数属性。关键是,实际继承的使用范围非常狭窄,而且大多数原因都是通过使用has-a casesThanks更好,这非常有帮助。我想我想要实现的是,在javascript中,我可以通过向prototype类添加方法来向预先存在的对象添加功能。或者,在clojure中,我可以扩展现有类型以实现一个协议,在该协议中,我的函数将根据传递的数据类型以不同的方式执行。在面向对象python中实现这种多态性的惯用方法是什么?