在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中实现这种多态性的惯用方法是什么?