我正在尝试向库中添加一些非常基本的功能(即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
...
最佳答案
问题是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
。