如何在 Python 中正确覆盖和调用超方法

Posted

技术标签:

【中文标题】如何在 Python 中正确覆盖和调用超方法【英文标题】:How to correctly override and call super-method in Python 【发布时间】:2015-06-30 09:28:05 【问题描述】:

首先,手头的问题。我正在为 scikit-learn 类编写一个包装器,并且在使用正确的语法时遇到了问题。我想要实现的是覆盖fit_transform 函数,它只稍微改变输入,然后用新参数调用它的super-方法:

from sklearn.feature_extraction.text import TfidfVectorizer

class TidfVectorizerWrapper(TfidfVectorizer):
    def __init__(self):
        TfidfVectorizer.__init__(self)  # is this even necessary?

    def fit_transform(self, x, y=None, **fit_params):
        x = [content.split('\t')[0] for content in x]  # filtering the input
        return TfidfVectorizer.fit_transform(self, x, y, fit_params)  
                            # this is the critical part, my IDE tells me for
                            # fit_params: 'unexpected arguments'

程序到处崩溃,从Multiprocessing exception 开始,并没有告诉我任何有用的信息。我该如何正确地做到这一点?

附加信息:我需要以这种方式包装它的原因是因为我使用sklearn.pipeline.FeatureUnion 来收集我的特征提取器,然后再将它们放入sklearn.pipeline.Pipeline。这样做的结果是,我只能为所有特征提取器提供一个数据集——但不同的提取器需要不同的数据。我的解决方案是以易于分离的格式提供数据,并在不同的提取器中过滤不同的部分。如果这个问题有更好的解决方案,我也很乐意听到。

编辑 1: 添加** 来解压字典似乎并没有改变任何东西:

编辑 2: 我刚刚解决了剩下的问题——我需要删除构造函数重载。显然,通过尝试调用父构造函数,希望正确启动所有实例变量,我做了完全相反的事情。我的包装器不知道它可以期待什么样的参数。一旦我删除了多余的电话,一切都完美无缺。

【问题讨论】:

您是否尝试将 return TfidfVectorizer.fit_transform(self, x, y, fit_params) 替换为 return TfidfVectorizer.fit_transform(self, x, y, **fit_params) 还取决于 y 在方法签名中的位置,可能需要“命名”y: return TfidfVectorizer.fit_transform(self, x, y=y, **fit_params) @KlausD。同意,但 fit_params 几乎可以肯定不适合传入,因为它是一本字典 我现在都尝试了,添加 **fit_params 反而什么也没改变,在尝试时完全相同的 IDE 警告和运行时异常。添加 y=y 会产生语法错误,因为在关键字之后输入了 fit_params。改变它会产生另一个语法错误。 【参考方案1】:

您忘记解压作为dict 传递的fit_params,而您想将其作为keyword arguments 传递,这需要解压运算符**

from sklearn.feature_extraction.text import TfidfVectorizer

class TidfVectorizerWrapper(TfidfVectorizer):

    def fit_transform(self, x, y=None, **fit_params):
        x = [content.split('\t')[0] for content in x]  # filtering the input
        return TfidfVectorizer.fit_transform(self, x, y, **fit_params)  

除了直接调用TfidfVectorizerfit_transform 之外,还可以通过super 方法调用重载版本

from sklearn.feature_extraction.text import TfidfVectorizer

class TidfVectorizerWrapper(TfidfVectorizer):

    def fit_transform(self, x, y=None, **fit_params):
        x = [content.split('\t')[0] for content in x]  # filtering the input
        return super(TidfVectorizerWrapper, self).fit_transform(x, y, **fit_params)  

要了解它,请查看以下示例

def foo1(**kargs):
    print kargs

def foo2(**kargs):
    foo1(**kargs)
    print 'foo2'

def foo3(**kargs):
    foo1(kargs)
    print 'foo3'

foo1(a=1, b=2)

它打印字典'a': 1, 'b': 2

foo2(a=1, b=2)

同时打印字典和foo2,但是

foo3(a=1, b=2)

在我们向foo1 发送一个与我们的字典相等的位置参数 时引发错误,它不接受这样的事情。但是我们可以这样做

def foo4(**kargs):
    foo1(x=kargs)
    print 'foo4'

工作正常,但打印一个新字典'x': 'a': 1, 'b': 2

【讨论】:

可能值得添加一两个关于 super() 的词来回答 OP 的其他问题 我尝试了您的建议,但仍然收到完全相同的错误。我也在主帖中添加了评论。 我确实删除了构造函数,也许应该加下划线

以上是关于如何在 Python 中正确覆盖和调用超方法的主要内容,如果未能解决你的问题,请参考以下文章

如何正确覆盖克隆方法?

RDD take()方法如何在内部工作?

方法如何在 Ruby 中使用哈希参数?

如何在多重继承中调用超类方法?

Python入门精华-OOP调用父类的方法及MRO方法解析序列

如何取消超类对默认 Matlab 函数的覆盖