在python中创建包装类时,如何让超类的方法返回我的包装类的实例

时间:2014-03-19 21:57:11

标签: python inheritance pandas

例如,如果我围绕pandas数据框创建一个简单的包装器:

    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

它有效。但是,如果我在其上调用一个返回数据帧的数据帧方法,那么它就不是我的包装类的实例。

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

如何让它为所有DataFrame方法返回MyDataFrame的实例?这是一个常见的问题吗?我是以错误的方式接近这个吗?

2 个答案:

答案 0 :(得分:2)

您展示的示例不是包装器,而是Python中的子类。现在,python子类和方法解析在你的情况下通过简单的规则行事

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

    1. x被定义为类MyDataFrame的对象 - 简单。显然,根据定义,type(x)MyDataFrame
    2. 在调用add期间,它会查看接收器对象,即x类的MyDataFrame。实际上,这个类定义了方法add。所以,它只返回该方法的结果,奇怪的是,尝试调用DataFrame([1, 2, 3]).add(1, 2)。结果会有所不同,因为它会查看add类中定义的pandas.DataFrame方法。
    3. 现在是第三部分 - 让我们应用相同的推理。 reindex中未定义MyDataFrame。我们下一步应该去哪儿?类层次结构,表示pandas.DataFrame。现在reindex确实由此类定义,它返回一个pandas.DataFrame对象!。 (见http://pandas.pydata.org/pandas-docs/dev/generated/pandas.DataFrame.reindex.html#pandas.DataFrame.reindex)所以,难怪ypandas DataFrame
    4. 现在,我首先要通过扩展pandas DataFrame来了解你的工作。像这样扩展不是常见的做法。如果您提供了您想要做的详细信息,我们可以提供解决方案。

      编辑:您的原始问题与扩展方法或扩展对象有关(C#有它们,正如您正确指出的那样,JS原型为您提供相同的功能.Python没有扩展方法/对象作为一等成员。已经就此进行了讨论。例如python extension methods

答案 1 :(得分:0)

在Pandas中有几个案例,这些类没有很好地实现,以形成派生类的基础。其中一些问题已得到修复,例如https://github.com/pydata/pandas/pull/4271https://github.com/pydata/pandas/issues/60

可以实现父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中,`_constructor'在内部设置以控制返回的类型。设置此类属性似乎可以解决问题:

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'>