我正在构建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;所以有两个问题:
更新--我刚刚注意到,如果不使用快速实例化(即res = Derived()而不是res = Derived.__new__(Derived)属性),就会像预期的那样工作。当然,我现在也失去了快速实例化。
我可以把蛋糕也吃了吗?
发布于 2022-11-20 11:46:44
所以有几个问题在这里起作用:
编译器错误是由于包含一个值的属性声明造成的。让它们与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对象引用增量和递减,我不确定这是完全必要的。
https://stackoverflow.com/questions/74500553
复制相似问题