Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/xslt/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 扩展现有第三方库的现有类的功能的最佳方法是什么?_Python_Oop_Inheritance - Fatal编程技术网

Python 扩展现有第三方库的现有类的功能的最佳方法是什么?

Python 扩展现有第三方库的现有类的功能的最佳方法是什么?,python,oop,inheritance,Python,Oop,Inheritance,比如说,我在代码中使用了大量的pandas.DataFrames。这是一个非常大的类,有很多片段,它有一个非常实用的API,您可以将它的方法调用链接在一起 感觉能够轻松地将功能添加到这个现有类中,同时保持功能性的、流动的API是很好的 我想把这个类转换成一些特定于领域的东西,以便删除很多在使用这个类时经常使用的特定于领域的样板代码 比如说,在我的梦中,我能做这样的事情: pandas.read_csv('sales.csv') \ .filter(items=['one', 'three

比如说,我在代码中使用了大量的
pandas.DataFrames
。这是一个非常大的类,有很多片段,它有一个非常实用的API,您可以将它的方法调用链接在一起

感觉能够轻松地将功能添加到这个现有类中,同时保持功能性的、流动的API是很好的

我想把这个类转换成一些特定于领域的东西,以便删除很多在使用这个类时经常使用的特定于领域的样板代码

比如说,在我的梦中,我能做这样的事情:

pandas.read_csv('sales.csv') \
    .filter(items=['one', 'three']) \
    .apply(myTransformationFunction) \
    .saveToHivePartition(tablename = 'sales', partitionColumn = 'four') \
    .join(pandas.read_csv('employees.csv')) \
    .filter(items=['one','three','five']) \
    .saveToHivePartition(tablename = 'EmployeeMetrics', partitionColumn = 'ten')
SuperDataFrame(pandas.read_csv('sales.csv')) \
    .df.filter .... \
    .df.apply .... \
    .saveToHivePartition .... \
    .df.join .... \
    .df.filter .... \
    .saveToHivepartition ....
在本例中,
saveToHivePartition
是一个自定义方法,它做了大量工作,以原子方式将某些内容保存到HDFS的正确位置,然后将该信息添加到配置单元元数据存储中。超级有用

显然,“简单”的答案是创建一个独立的函数,我可以将
DataFrame
对象与我需要的其他参数一起传递到该函数中。每次我想要执行save时,我都需要将dataframe封装到一个变量中,然后将该变量传递到一个单独的函数中。远没有上面的例子那么干净

所以接下来想到的是创建一些很酷的新的
SuperDataFrame
类,它是
DataFrame
的超集。这种方法的唯一问题是:你是如何构造它的?我无法更改
pandas。请阅读\u csv()
开始返回我的新类。我不能将基类强制转换为子类。我可以拥有
SuperDataFrame
be。。。dataframe类的包装器?但我认为,每当我想调用一个基本的
数据帧
函数时,它必须是这样的:

pandas.read_csv('sales.csv') \
    .filter(items=['one', 'three']) \
    .apply(myTransformationFunction) \
    .saveToHivePartition(tablename = 'sales', partitionColumn = 'four') \
    .join(pandas.read_csv('employees.csv')) \
    .filter(items=['one','three','five']) \
    .saveToHivePartition(tablename = 'EmployeeMetrics', partitionColumn = 'ten')
SuperDataFrame(pandas.read_csv('sales.csv')) \
    .df.filter .... \
    .df.apply .... \
    .saveToHivePartition .... \
    .df.join .... \
    .df.filter .... \
    .saveToHivepartition ....
我甚至认为这是行不通的,因为这是假设每当pandas执行一个函数时,它都会在适当的位置变异
数据帧
,我很确定它甚至不会这样做


有什么想法吗?或者这只是一件坏事?

一种方法是扩展
DataFrame
类。要保持流畅的界面,您只需创建自己的
custom.read\u csv

def read_csv(file):
    dataframe = pandas.read_csv(file)
    return SuperDataFrame(dataframe)
首先调用该函数,其余函数调用保持原样。您只需将所需的函数添加到新的数据帧中

或者(特定于python的黑客攻击),您可能可以在导入它的模块中将自己的函数直接猴子补丁到原始dataframe类中,但不要…。

您可以使用来确保pandas操作返回新类的实例。一个简单的例子:

class MyDF(pandas.DataFrame):
    @property
    def _constructor(self):
        return MyDF

    def myMethod(self):
        return "I am cool"

>>> d = MyDF([[1, 2, 3], [4, 5, 6], [7, 8, 9]], columns=["A", "B", "C"])
>>> d.filter(["A", "B"]).apply(lambda c: c**2).myMethod()
'I am cool'
当然,您还必须创建自己的Series子类,并可能定义是否还希望能够处理Series

也就是说,我不清楚你的例子是否真的证明了这一点。您已经可以在普通数据帧上链接方法调用,因此,如果您只想保存一些中间阶段,那么这样做就没有那么繁重了:

data = pandas.read_csv('sales.csv') \
    .filter(items=['one', 'three', 'five']) \
    .apply(myTransformationFunction)

data2 = data.join(pandas.read_csv('employees.csv')) \
    .filter(items=['one', 'three'])

saveToHivePartition(data, tablename='sales', partitionColumn='four')
saveToHivePartition(data2, tablename='EmployeeMetrics', partitionColumn='ten')
(由于我在对问题的评论中提到的问题,我更改了过滤器的顺序。先过滤到较小的子集,然后再过滤到较大的子集是没有意义的;较大的子集稍后不会出现,因为其中一些已经被过滤掉了。)

仅仅能够将事物写成方法链本身并不是一个优势。我尤其怀疑像您的
.saveToHivePartition
这样的链接方法,这些方法可能只是因为它们的副作用才被调用的。当方法以装配线方式运行时,链接方法是有意义的,每个方法都接受前一个方法的输入并修改它以传递给下一个。但是,使用只产生副作用并返回未更改对象的方法并不能使代码更具可读性


另外,请注意,此解决方案是针对熊猫的。通常,如果某些库中的类创建了彼此的实例,则必须仔细设计库,以允许以保留类之间关系的方式进行子类化。Pandas已经用我描述的构造函数重写机制完成了这项工作,但它并不总是这样,在那之前,你很难做到你想做的事情。

是的,这个问题几乎离题了,但我仍然希望看到它能给出的答案。我认为,不幸的是,
pandas
只是一个库的例子,它的设计方式很难使用继承进行扩展。无论如何,我很少在Python中看到这种链式方法调用。你是对的,
DataFrame
大多数时候都不会被修改,除非你明确地把它作为参数传递,而且不是所有的方法都有这个选项——例如,
apply
。我认为你写的例子没有意义,因为你先过滤到“一”和“三”,然后过滤到“一”、“三”和“三”“五”,但是如果你已经过滤到只有“一”和“三”,那么“五”就不存在了。@BernBarn假定“五”“是由join引入的。啊,我也考虑过这个答案,但是
超级数据帧会是什么样子的呢?\uuu init\uuuu
函数会是什么样子,这样
超级数据帧
继承了
数据帧
的每个属性?我认为在
\uuuu init\uuuu
调用中调用
self=DataFrame
这样简单/愚蠢的事情是做不到的。如果它像调用SuperDataFrame(dateframe)一样简单,我甚至不需要自定义函数,我只需调用
SuperDataFrame(pandas.read_csv())
@JHixson在这种情况下,您只需继承
\uuuu init\uuuuuuuuuuuu
-我绝对不会乱搞
\uuuu init\uuuuuuuuuuuuuuu
,但是你可以添加方法,比如
save\u to\u hive\u partition
我同意,把它留给超类init,然后添加你需要的方法want@juanpa.arrivillaga