Cython扩展类型支持类属性吗?


问题内容

Python类可以具有类属性:

class Foo(object):
   bar = 4

是否存在用于在Cython扩展类型中定义类属性的类似构造?例如,当我尝试编译以下cython代码时

cdef class Foo:
    cdef int bar
    bar = 4

我收到此错误:

thing.c:773:3: error: use of undeclared identifier 'bar'
  bar = 4;
  ^
1 error generated.
error: command 'cc' failed with exit status 1

问题答案:

简短的答案是是和不是。

不,没有方便的语法惯用法可用于在中快速插入class属性cdef class。但是..

整个问题cython在于,它为您提供了较低级别的访问权限。付出额外努力的通常动机是表现,但您也可以C通过额外的自由来做类似的事情。困难在于,存在许多陷阱,在这种情况下,如果python不进行大量工作就不会获得纯类属性。但是,获得简单用例所需的东西非常容易。

例如,假设我将某个计算引擎作为一个类,并且希望为所有实例全局设置返回值的精度。我希望在编译时使用默认值,有时我可能希望将其调低以快速处理一些试验,然后为我的最终工作将其调高。类属性是按顺序制作的,但是您可以通过以下方式获得所需的功能cython

首先,在模块级别定义以下内容:

cdef int _precision[1]  # storage for my class 'attribute'
_precision[0]=8         # my default value, set during compilation

使用数组允许我们使用与C等效的cython惯用法。由于数据项是一个数组,因此该名称隐式是一个指针。这允许使用语法习语从存储位置转换为python引用。如果只需要一个全局常量,该常量可以由模块中任何类中的代码访问,则说明您已经完成。如果要严格将其用作类属性,则必须强制执行该规则-
编译器不在乎。precision[0]``*precision``cdef``precision``cython``cython``cdef

现在,如果您还想从python代码中调整值,则需要使用模块cdef中的python代码可以调用的一对函数来访问“属性”:

cdef int*  get_precision(): return _precision
cdef void* set_precision(int i): _precision[0]=i

此时python,除非您真的想出汗,否则语义将与pure有所不同。您需要一个pythonsetter和getter函数,我发现python用properties实现的描述符协议是最简单的:

cdef class SomeCalculator:
  ...

  property precision:
    def __get__(self):
      """Get or set calculation precision, default == 8.
         This is like a class attribute: setting affects all instances,
         however, it also affects all subclasses."""
      return get_precision()[0]
    def __set__(self,int integer): set_precision(min(30,max(0,integer)))

第一个获取对“属性”的python引用。第二个参数将“属性”设置为python整数值,并规定要处于限制范围之内。该cython函数调用和返回接口自动进行转换,这比他们看起来更复杂的照顾。

例如,get_precision返回C-pointer。如果您进行了取消引用,则get_precision尝试返回in时会收到错误消息C-int__get__就像返回python。如果相反,您只是省略了[0]取消引用,则在__get__尝试返回a时会出现错误,C-pointer就好像它是一个python int。按照书面规定,自动转换可以正确匹配类型。
cython对这种事情非常挑剔,并且可以静默返回不正确的值,这些值只能在运行时发现。可能需要做一些实验才能推断出正确的咒语。

该文档字符串告诉您不要期望纯python类属性。如果您想对子类进行分类,并且让子类使用其他全局设置,则需要多花点功夫。在中python,所有这些操作都是自动完成的。

即使这样,仍然存在其他差异。可以在类或实例上引用真实的类属性。此属性只能在实例上引用。在实例上设置真实的类属性会创建一个实例特定的副本,而类属性保持不变,但对更改后的实例不可见。

对于给定的用例,这可行。真正的类属性是不必要的。由于cython代码通常不是那么抽象,而且计算量很大,因此这种最小的方法通常就足够了。