使用 __new__ 在 Cython 扩展类型中子类化 int

问题描述 投票:0回答:1

我想对使用“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__
...

cython subclassing
1个回答
0
投票

这听起来不像是

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 没有检查来阻止你这样做。

© www.soinside.com 2019 - 2024. All rights reserved.