python 一个cython子类可以访问它的cython超类的私有属性吗?其他cython类呢?

slwdgvem  于 2022-11-21  发布在  Python
关注(0)|答案(1)|浏览(182)

我正在构建cython扩展类型,我一直很烦恼,我必须公开类属性,以便其他扩展类型能够看到它们。但现在我也在创建子类,我甚至更惊讶。
下面的代码

@cython.cclass
class Base:
    base_attrib = cython.declare(cython.double, 0.0)

@cython.cclass
class Derived(Base):
    derived_attrib = cython.declare(cython.double, 0.0)

@cython.cfunc
def f_on_derived(a: Derived, b: Derived) -> Derived:
    res = Derived.__new__(Derived)
    ad = a.derived_attrib
    bd = b.derived_attrib
    ab = a.base_attrib
    bb = b.base_attrib
    res.derived_attrib = ad + bd
    res.base_attrib = ab + bb
    return res

生成一个.c文件,但编译器随后会发出抱怨

src/crujisim/cythontests.c(40975): error C2065: 'base_attrib': undeclared identifier
src/crujisim/cythontests.c(40975): warning C4244: '=': conversion from 'double' to 'int', possible loss of data
src/crujisim/cythontests.c(41007): error C2065: 'derived_attrib': undeclared identifier
src/crujisim/cythontests.c(41007): warning C4244: '=': conversion from 'double' to 'int', possible loss of data

由于它是一个C函数,我本以为类型注解就足够了,但事实并非如此。
我可以通过声明公共可见性使其编译,例如

@cython.cclass
class Base:
    base_attrib = cython.declare(cython.double, visibility='public')

@cython.cclass
class Derived(Base):
    derived_attrib = cython.declare(cython.double, visibility='public')

但是res.base_attrib = ab + bb的C代码必须通过python,比如

__pyx_t_1 = PyFloat_FromDouble((__pyx_v_ab + __pyx_v_bb))
if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 26, __pyx_L1_error)__Pyx_GOTREF(__pyx_t_1)
if (__Pyx_PyObject_SetAttrStr(__pyx_v_res, __pyx_n_s_base_attrib, __pyx_t_1) < 0) __PYX_ERR(0, 26, __pyx_L1_error)__Pyx_DECREF(__pyx_t_1)
__pyx_t_1 = 0;

所以有两个问题:

  • 我能不能拥有一个只有C语言才能访问的超类属性?
  • 我可以拥有只在C中的类属性,并且在其他示例中对C代码可见吗?
    Update我刚刚注意到如果不使用快速示例化,res = Derived()而不是res = Derived.__new__(Derived)属性会按预期工作。当然,我现在也失去了快速示例化。

我能不能把蛋糕也吃了?

t40tm48m

t40tm48m1#

这里有几个问题:
编译器错误是由于属性声明中包含一个值引起的。将它们保留为base_attrib = cython.declare(cython.double),将删除警告,并将值自动初始化为0。
另一个问题是,通过快速示例化生成的对象必须通过python来访问其属性,而python示例化不需要。这是因为__new__方法生成的是python对象,而不是C版本。所以在代码中,唯一的问题是访问新示例的属性,而不是作为参数传递的属性。
通过声明保存快速示例化返回的对象的变量解决了这个问题。
因此,原始问题的最快版本是

@cython.cclass
class Base:
    base_attrib = cython.declare(cython.double)

@cython.cclass
class Derived(Base):
    derived_attrib = cython.declare(cython.double)

@cython.cfunc
def f_on_derived(a: Derived, b: Derived) -> Derived:
    res: Derived = Derived.__new__(Derived)  # Notice res: Derived
    ad = a.derived_attrib
    bd = b.derived_attrib
    ab = a.base_attrib
    bb = b.base_attrib
    res.derived_attrib = ad + bd
    res.base_attrib = ab + bb
    return res

为了测试速度我有这两个函数

@cython.ccall
def python_instantiate() -> Derived:
    o = Derived()
    return o

@cython.ccall
def cython_fast_instantiate() -> Derived:
    o: Derived = Derived.__new__(Derived)
    return o

然后计算它们的时间

In [2]: %timeit python_instantiate()
87.5 ns ± 0.895 ns per loop (mean ± std. dev. of 7 runs, 10,000,000 loops each)

In [3]: %timeit cython_fast_instantiate()
62.1 ns ± 0.574 ns per loop (mean ± std. dev. of 7 runs, 10,000,000 loops each)

这证明了快速示例化更快,即使有一些python对象引用增加和减少,我不确定是否完全必要。

相关问题