Cython 扩展类型是否支持 class 属性?
Do Cython extension types support class attributes?
Python classes 可以有 class 个属性:
class Foo(object):
bar = 4
是否有用于在 Cython 扩展类型中定义 class 属性的类似结构?例如,当我尝试编译以下 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
你不能那样做。我不知道是否支持静态属性,但是必须从方法访问 "normal" 属性,例如构造函数:
cdef class Foo:
cdef int bar
def __init__(self):
self.bar = 4
虽然似乎不可能具有 C 类型的静态属性,但 Cython 扩展类型可以具有常规的 Python 静态属性,这些属性也可以在 Python 中自动访问。就像在 Python:
中声明它们一样声明它们
cdef class Foo:
bar = 4
生成的代码显示这些静态属性作为 Python 对象存储在 class 对象的属性字典中,即如果您在使用 C 类型的上下文中使用它们,它们从 Python 个对象转换回来。
简短的回答是和否。
不,没有方便的句法习语可用于在 cdef class
中快速插入 class 属性。然而....
cython
的全部意义在于它为您提供较低级别的访问权限。额外努力的通常动机是性能,但您也可以用额外的自由来做 C
之类的事情。困难在于,有很多陷阱,在这种情况下,如果不进行大量工作,您将无法获得纯 python
class 属性。尽管如此,对于简单的用例来说,还是很容易获得所需的东西。
例如,假设我正在将一些计算引擎作为 class 并且我希望全局设置所有实例的 return 值的精度。我想要一个编译时的默认值,有时我可能想将它调低以快速处理一些试验,然后在我的最终工作中将它调高。 class 属性是定制的,但您可以在 cython
中获得所需的功能,如下所示:
首先,在模块级别定义以下内容:
cdef int _precision[1] # storage for my class 'attribute'
_precision[0]=8 # my default value, set during compilation
使用数组允许我们使用 cython
习语 precision[0]
,它等同于 C *precision
。 cdef
名称 precision
隐式是一个指针,因为数据项是一个数组。这允许使用 cython
语法习语将 cython
存储位置转换为 python 引用。如果您想要的只是一个可以被模块中任何 class 中的 cdef
代码访问的全局常量,那么您就完成了。如果您想严格将其用作 class 属性,则必须强制执行该规则 - 编译器不关心。
现在,如果您还想调整来自 python
代码的值,您将需要一对 cdef
模块中的 python
代码可以调用的函数来访问 'attribute':
cdef int* get_precision(): return _precision
cdef void* set_precision(int i): _precision[0]=i
在这一点上,语义会与纯粹的 python
有所不同,除非你真的想出汗。你需要一个 python
setter 和 getter 函数,我发现属性实现的 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)))
第一个获得对 'attribute' 的 python 引用。第二个将 'attribute' 设置为 python
整数值,限制在限制范围内。 cython
函数调用和 return 界面自动处理转换,这比看起来更复杂。
例如,get_precision
return 是 C-pointer
。如果您在 get_precision
中进行了取消引用,您会在尝试 return 一个 __get__
中的 C-int
时遇到错误,就好像它是 python
一样。相反,如果您只是省略 __get__
中的 [0]
取消引用,您将在尝试 return 一个 C-pointer
时遇到一个错误,就好像它是一个 python int
一样。正如所写,自动转换正确匹配类型。 cython
对这类事情非常挑剔,可以默默 return 不正确的值,只能在运行时发现。可能需要一些实验才能推断出正确的咒语。
文档字符串告诉您不要期待纯 python
class 属性。如果你想要 sub-class,并且让 sub-classes 使用不同的全局设置,你需要付出更多的努力。在 python
中,所有这些都是自动完成的。
即便如此,还是有其他的区别。可以在 class 或实例上引用真实的 class 属性。此 属性 只能在实例上引用。在实例上设置真实的 class 属性会创建实例特定的副本,使 class 属性保持不变,但对更改后的实例不可见。
对于给定的用例,这可行。不需要真正的 class 属性。由于 cython
代码通常不那么抽象和计算密集,因此这种最小的方法通常就足够了。
Python classes 可以有 class 个属性:
class Foo(object):
bar = 4
是否有用于在 Cython 扩展类型中定义 class 属性的类似结构?例如,当我尝试编译以下 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
你不能那样做。我不知道是否支持静态属性,但是必须从方法访问 "normal" 属性,例如构造函数:
cdef class Foo:
cdef int bar
def __init__(self):
self.bar = 4
虽然似乎不可能具有 C 类型的静态属性,但 Cython 扩展类型可以具有常规的 Python 静态属性,这些属性也可以在 Python 中自动访问。就像在 Python:
中声明它们一样声明它们cdef class Foo:
bar = 4
生成的代码显示这些静态属性作为 Python 对象存储在 class 对象的属性字典中,即如果您在使用 C 类型的上下文中使用它们,它们从 Python 个对象转换回来。
简短的回答是和否。
不,没有方便的句法习语可用于在 cdef class
中快速插入 class 属性。然而....
cython
的全部意义在于它为您提供较低级别的访问权限。额外努力的通常动机是性能,但您也可以用额外的自由来做 C
之类的事情。困难在于,有很多陷阱,在这种情况下,如果不进行大量工作,您将无法获得纯 python
class 属性。尽管如此,对于简单的用例来说,还是很容易获得所需的东西。
例如,假设我正在将一些计算引擎作为 class 并且我希望全局设置所有实例的 return 值的精度。我想要一个编译时的默认值,有时我可能想将它调低以快速处理一些试验,然后在我的最终工作中将它调高。 class 属性是定制的,但您可以在 cython
中获得所需的功能,如下所示:
首先,在模块级别定义以下内容:
cdef int _precision[1] # storage for my class 'attribute'
_precision[0]=8 # my default value, set during compilation
使用数组允许我们使用 cython
习语 precision[0]
,它等同于 C *precision
。 cdef
名称 precision
隐式是一个指针,因为数据项是一个数组。这允许使用 cython
语法习语将 cython
存储位置转换为 python 引用。如果您想要的只是一个可以被模块中任何 class 中的 cdef
代码访问的全局常量,那么您就完成了。如果您想严格将其用作 class 属性,则必须强制执行该规则 - 编译器不关心。
现在,如果您还想调整来自 python
代码的值,您将需要一对 cdef
模块中的 python
代码可以调用的函数来访问 'attribute':
cdef int* get_precision(): return _precision
cdef void* set_precision(int i): _precision[0]=i
在这一点上,语义会与纯粹的 python
有所不同,除非你真的想出汗。你需要一个 python
setter 和 getter 函数,我发现属性实现的 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)))
第一个获得对 'attribute' 的 python 引用。第二个将 'attribute' 设置为 python
整数值,限制在限制范围内。 cython
函数调用和 return 界面自动处理转换,这比看起来更复杂。
例如,get_precision
return 是 C-pointer
。如果您在 get_precision
中进行了取消引用,您会在尝试 return 一个 __get__
中的 C-int
时遇到错误,就好像它是 python
一样。相反,如果您只是省略 __get__
中的 [0]
取消引用,您将在尝试 return 一个 C-pointer
时遇到一个错误,就好像它是一个 python int
一样。正如所写,自动转换正确匹配类型。 cython
对这类事情非常挑剔,可以默默 return 不正确的值,只能在运行时发现。可能需要一些实验才能推断出正确的咒语。
文档字符串告诉您不要期待纯 python
class 属性。如果你想要 sub-class,并且让 sub-classes 使用不同的全局设置,你需要付出更多的努力。在 python
中,所有这些都是自动完成的。
即便如此,还是有其他的区别。可以在 class 或实例上引用真实的 class 属性。此 属性 只能在实例上引用。在实例上设置真实的 class 属性会创建实例特定的副本,使 class 属性保持不变,但对更改后的实例不可见。
对于给定的用例,这可行。不需要真正的 class 属性。由于 cython
代码通常不那么抽象和计算密集,因此这种最小的方法通常就足够了。