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
解决方法:
简短的答案是是和不是.
不,没有方便的语法习惯用法,可以在cdef类中快速插入类属性.但是..
cython的全部要点是它为您提供了较低级别的访问权限.付出额外努力的通常动机是性能,但是您也可以额外*地执行类似C的事情.困难在于,存在许多陷阱,在这种情况下,如果不进行大量工作就不会获得纯python类属性.但是,获得简单用例所需的东西非常容易.
例如,假设我将某个计算引擎作为一个类,并且希望为所有实例全局设置返回值的精度.我想在编译时使用默认值,有时我可能想将其调低一些,以快速处理一些试验,然后为我的最终工作将其调高一些.类属性是按顺序制作的,但是您可以在cython中获得所需的功能,如下所示:
首先,在模块级别定义以下内容:
cdef int _precision[1] # storage for my class 'attribute'
_precision[0]=8 # my default value, set during compilation
使用数组允许我们使用cython习惯用法精度[0],它等效于C *精度.由于数据项是一个数组,因此cdef名称精度隐式是一个指针.这允许使用cython语法惯用法将cython存储位置转换为python引用.如果只需要一个全局常量,而该常量可以由模块中任何类中的cdef代码访问,则说明您已经完成.如果要严格将其用作类属性,则必须强制执行该规则-编译器不在乎.
现在,如果您还想从python代码中调整值,则需要一对cdef函数,模块中的python代码可以调用这些函数来访问“属性”:
cdef int* get_precision(): return _precision
cdef void* set_precision(int i): _precision[0]=i
此时,除非您真的想出汗,否则其语义将与纯python有所不同.您需要一个python setter和getter函数,并且我发现由properties实现的python描述符协议是最简单的:
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指针.如果您在get_precision中进行了解引用,则尝试在__get__中返回C-int就像是python一样,将会收到错误消息.相反,如果您只是省略了__get__中的[0]取消引用,则在尝试返回C指针(就像它是python int一样)时会出错.按照书面规定,自动转换可以正确匹配类型. cython对这种事情非常挑剔,可以静默返回不正确的值,这些值只能在运行时发现.可能需要做一些实验才能推断出正确的咒语.
docstring告诉您不要期望纯python类属性.如果您想进行子类化,并且让子类使用其他全局设置,则需要多花点功夫.在python中,所有这些操作都是自动完成的.
即使这样,仍然存在其他差异.可以在类或实例上引用真实的类属性.此属性只能在实例上引用.在实例上设置一个真实的类属性会创建一个实例特定的副本,而类属性保持不变,但对更改后的实例不可见.
对于给定的用例,这可行.真正的类属性是不必要的.由于cython代码通常不那么抽象,而且计算量大,因此这种最小的方法通常就足够了.