我想对使用“int”子类的代码进行 cythonize,它的行为实际上就像一个集合(类似于 C++ bitset)。
纯python对象用
__new__
实例化:
class BitSet(int):
def __new__(cls, intlist=()):
val = 0
for k in intlist:
val |= (1<<k)
return super().__new__(cls, val)
但是,如果我尝试像这样使它成为 Cython 类型的扩展:
cdef class BitSet(int):
def __new__(cls, intlist=()):
# ...
编译器抛出错误:
__new__ method of extension type will change semantics in a future version of Pyrex and Cython. Use __cinit__ instead.
__cinit__
和__init__
的文档后,我不知道如何使用它们,因为__cinit__
不应该返回值。
但是,我尝试了以下编译:
cdef class BitSet(int):
cdef BitSet __new__(cls, intlist=()):
# ...
像下面这样使用
__cinit__
也可以编译,但我不确定它是否有效,因为看起来我们实例化了一个基础int
:
cdef class BitSet(int):
def __cinit__(self, intlist=()):
val = 0
for k in intlist:
val |= (1<<k)
self = val
你会用什么方法来 cythonize 这样的类,它真的相关吗?有关更多详细信息,我希望我的类具有以下方法,其行为类似于集合:
__len__
:设置为 1 的位数(即集合中的元素数)__bool__
:如果集合不为空则为真__iter__
:遍历整数元素__contains__
:测试整数是否在位集中__sub__
:集合的差异而且我还想禁用一些不适合的int方法,例如
__float__
,__neg__
,__pow__
...
这听起来不像是
int
的子类,因为它与 int
具有截然不同的接口。它不能(或不应该)真正用于代替int
。听起来更像是用int
来实现的。所以也许你应该这样做?
你是对的,
__cinit__
没有做你想做的事。您带有 self = val
的版本肯定行不通 - 它只会在函数中重新分配 self
引用。
在 Cython 3 中,无论如何你都会坚持使用
cdef class
因为:
TypeError: inheritance from PyVarObject types like 'int' not currently supported
在 Cython 0.29.x 中,你可以摆脱它,因为它没有被检查。但是,如果您向该类型添加任何内容(例如
cdef
成员、非静态 cdef
方法),它就会分崩离析。太容易坏了,真心不推荐。基本上你添加到类中的任何东西都会覆盖和破坏底层int
.的数据
如果你非要用一个
cdef class
可能是一个staticmethod
构造函数:
cdef class BitSet(int)
@staticmethod
def make_bitset(intlist):
val = 0
for k in intlist:
val |= (1<<k)
return Bitset(val)
这当然不会阻止人们自己调用构造函数。
你显然可以使用非
cdef class
,那会起作用。
总而言之,这并不是继承的真正“正确”使用,您想要做的事情并不是 真的
cdef classes
支持,并且使用 cdef class
为您提供了破坏数据和破坏事物的巨大机会,因为旧版本的 Cython 没有检查来阻止你这样做。