【问题标题】:__cinit__() takes exactly 2 positional arguments when extending a Cython class__cinit__() 在扩展 Cython 类时正好采用 2 个位置参数
【发布时间】:2017-12-03 22:23:15
【问题描述】:

我想扩展 scikit-learn 的 ClassificationCriterion 类,该类在内部模块 sklearn.tree._criterion 中定义为 Cython 类。我想在 Python 中执行此操作,因为通常我无法访问 sklearn 的 pyx/pxd 文件(所以我不能 cimport 他们)。但是,当我尝试扩展 ClassificationCriterion 时,我收到错误 TypeError: __cinit__() takes exactly 2 positional arguments (0 given)。下面的 MWE 重现了错误,并显示错误发生在__new__ 之后但在__init__ 之前。

有没有办法像这样扩展 Cython 类?

from sklearn.tree import DecisionTreeClassifier
from sklearn.tree._criterion import ClassificationCriterion

class MaxChildPrecision(ClassificationCriterion):
    def __new__(self, *args, **kwargs):
        print('new')
        super().__new__(MaxChildPrecision, *args, **kwargs)

    def __init__(self, *args, **kwargs):
        print('init')
        super(MaxChildPrecision).__init__(*args, **kwargs)

clf = DecisionTreeClassifier(criterion=MaxChildPrecision())

【问题讨论】:

  • 方法需要将 self 作为第一个位置参数。但是他们不应该在 super 调用中传递它,这就是为什么有必要在 *args 之外捕获它。
  • @DanielRoseman 谢谢,我更新了代码,但仍然出现同样的错误。
  • @user2357112 也感谢您。我更新了代码,但这还没有解决问题。还有其他想法吗?
  • 您需要定义__new__ 并传递超类__new__ 它期望的参数。与传递超类 __init__ 所需的参数相同 (although those arguments might not be the ones you expect it to expect)。

标签: python scikit-learn runtime-error subclass cython


【解决方案1】:

有两个问题。首先,ClassificationCriterion requires two specific arguments to its constructor that you aren't passing it。您必须弄清楚这些参数代表什么并将它们传递给基类。

其次,有一个 Cython 问题。如果我们查看the description of how to use __cinit__,我们会看到:

传递给构造函数的任何参数都将传递给__cinit__() 方法和__init__() 方法。如果您期望在 Python 中子类化您的扩展类型,您可能会发现给 __cinit__() 方法提供 *** 参数很有用,以便它可以接受和忽略额外的参数。否则,任何具有不同签名的 init() 的 Python 子类都必须覆盖 __new__()__init__()

不幸的是,sklearn 的作者没有提供*** 参数,所以你必须覆盖__new__。像这样的东西应该可以工作:

class MaxChildPrecision(ClassificationCriterion):
    def __init__(self,*args, **kwargs):
        pass

    def __new__(cls,*args,**kwargs):
        # I have NO IDEA if these arguments make sense!
        return super().__new__(cls,n_outputs=5,
                           n_classes=np.ones((2,),dtype=np.int))

我将必要的参数传递给__new__ 中的ClassificationCriterion,并在我认为合适的情况下处理__init__ 中的其余参数。我不需要调用基类__init__(因为基类没有定义__init__)。

【讨论】:

  • 非常感谢。我希望这不会花费您太多时间,因为当您发布它时,我几乎得到了相同的结果,但还没有准备好发布答案。看来我的情况的正确参数是n_outputs=1, n_classes=np.array([2], dtype=np.intp)。当我在 Python 中进行子类化并覆盖 node_impuritychildren_impurity 时,它们不会被调用。我的想法是否正确,因为这些方法是cdefs 而不是cpdefs?我现在在 Cython 中进行子类化,这似乎工作正常。
  • 是的 - 您无法在 Python 中访问 cdef 方法。如果您需要访问它们,听起来 Cython 中的子类化是可行的方法。
  • (2/2) 作为一个侧面不 / 以供将来参考:我添加了 a feature request to scikit-learn 以使标准的扩展更直接。一旦它正常工作,我将在那里发布我的代码,这样这个答案就可以成为这里的正确答案,未来的读者可以在那里寻找 scikit-learn 的特性。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-01-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多