是否可以在Python中从库中完全修补类?

时间:2016-06-01 13:17:44

标签: python scikit-learn

我正在尝试为库添加一些非常基本的功能(即scikit-learn)。但是,我宁愿不直接修改库本身,因为a)即使在我自己的测试中,我宁愿有能力打开和关闭功能b)这很可能是不属于的功能该库的主要代码库。

我想要做的是对基类进行修补(在本例中为sklearn.base.BaseEstimator),这样当库中的其他类从类导入/派生时,它们就会得到我修改过的类。这是我到目前为止所做的:

import sklearn
from sklearn.base import BaseEstimator
from sklearn import base

class InstrumentedEstimator(sklearn.base.BaseEstimator):
    def __init__(self, *args, **kwargs):
        print 'called'
        super(InstrumentedEstimator, self).__init__(*args, **kwargs)

sklearn.base.BaseEstimator = InstrumentedEstimator
base.BaseEstimator = InstrumentedEstimator
BaseEstimator = InstrumentedEstimator

from sklearn.ensemble import RandomForestClassifier
RandomForestClassifier()

这不起作用 - 即RandomForestClassifier()不会打印called。我怀疑这里的主要原因是,当查看RandomForestClassifier的层次结构时,从BaseEstimator派生的最终父类是sklearn.ensemble.base.BaseEnsemble。查看sklearn / ensemble / base.py,可以看到以下内容:

from ..base import BaseEstimator

甚至可以在Python中修补这种导入方式吗?更重要的是,是否有可能(显然是在一个程序的上下文中)对这个类的所有实例进行修补,无论它们在何处以及如何导入?

理想情况下,最终游戏会是这样的:

import my_module
from sklearn.(anything) import SomeEstimator

SomeEstimator()  # this runs my code in addition to SomeEstimator's code
...

1 个答案:

答案 0 :(得分:2)

问题在于BaseEstimator没有定义__init__方法,因此其子级不会调用super.__init__。但是,如果你使用补丁BaseEnsemble,你会看到效果。替换类不是很有效,因为其他类已经将子类子类化了,但是您可以像这样替换类上的方法:

from sklearn.ensemble import BaseEnsemble, RandomForestClassifier

old_init = BaseEnsemble.__init__

def new_init(*args, **kwargs):
    print 'called'
    old_init(*args, **kwargs)

BaseEnsemble.__init__ = new_init

RandomForestClassifier()

这确实打印called