【问题标题】:Mixing cdef and regular python attributes in cdef class在 cdef 类中混合 cdef 和常规 python 属性
【发布时间】:2020-10-11 19:37:29
【问题描述】:

我正在学习 Cython,现在正在尝试它。我尝试了基本的 cdef 类示例程序,它运行良好。

现在我想做的是在 cdef 类类型中混合 cdef 和非 cdef 混合属性,像这样

cdef class Context:
    cdef public int byteIndex, bitIndex

    def __init__(self, msg = "", msg_len = 0):
        self.msg = msg 
        self.msg_len = msg_len 
        self.byteIndex = 0
        self.bitIndex = 7

但是一旦我实例化对象我就会得到错误

!! AttributeError: 'c_asnbase.Context' object has no attribute 'msg'

这是否意味着一旦你用 cdef 定义了一个 python 类,所有 self.* 属性都必须被 cdef 定义?

【问题讨论】:

  • “现在我想做的是混合使用 cdef 和非 cdef 混合属性” - 为什么?
  • 因为我觉得类似字符串的属性在 python 中更容易处理。所以想知道我是否可以在 python 中保留字符串属性并仅将数字属性转换为 cdef。
  • "我觉得类似字符串的属性在 python 中更容易处理" - cdef 不会阻止 Python 访问你的属性。毕竟,您将它们标记为公开。

标签: python cython


【解决方案1】:

正如@DavidW 所指出的,cdef 类的问题在于它们没有__dict__。如果真的需要,您可以将__dict__ 添加到类定义中:

%%cython
cdef class A:
    cdef dict __dict__        # now the usual attribute look-up is possible
    cdef readonly int answer 
    def __init__(self):
        self.answer = 42             #cdef attribute
        self.question = "unknown"    #pure python attribute, possible

现在:

a=A()
print(a.answer)
# 42
print(a.question)
# 'unknown' 
a.question = 'Why?'
print(a.question)
# 'Why?' 
setattr(a, 'new_attr', None)
print(a.new_attr)
# None

注意:如果在定义 cdef class A 时不使用 __dict__ 而是使用 cdef public object question,则 setattr(a,'new_attr', None) 将不可能。

显然使用__dict__ 会产生额外的成本,因此只要性能很重要,就可能会使用预定义的属性。 cdef-classes 的优点之一是更小的内存占用(例如因为没有__dict__-slot)。因此,添加__dict__-slot 至少会抵消一些优势——人们应该问,另一种设计是否会是更好的选择——但显然在某些情况下,添加__dict__-slot 是有意义的。

另一种方法是创建cdef class 的子类并使用它而不是基类。


一旦定义了__dict__ 槽,A 类的实例将具有__dict__-属性(通常的cdef-类不是这种情况)。但是,__dict__ 不包含 cdef 属性,例如上例中的answer(无论它们是否公开)——只有普通的纯python属性(例如上例中的questionnew_attr)。

这里是上面的例子:

# no answer-attribute in __dict__:
a.__dict__
# {'new_attr': None, 'question': 'Why?'} 

注意:here is the part 在 Cython 文档中关于动态属性。

【讨论】:

    【解决方案2】:

    这是否意味着一旦你用 cdef 定义了一个 python 类,所有 self.* 属性都必须被 cdef 定义?

    是的。这在the documentation 中有非常明确的说明:

    cdef 类中的属性与常规类中的属性行为不同:

    • 所有属性都必须在编译时预先声明
    • ...

    您可以通过将属性定义为对象类型来非常愉快地存储字符串:

    cdef public object msg
    

    在内部,这样做的原因是cdef class 没有字典,这样可以节省空间并使属性访问更快,但这确实意味着它不能在运行时添加任意属性。这与在普通 Python 类中使用 __slots__ 相当相似。

    【讨论】:

    • @DavidW 我可以让public: 定义一段公共属性吗?
    • @datdinhquoc 我不这么认为,但你可以很容易地尝试一下,看看它是否有效
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-13
    • 1970-01-01
    • 1970-01-01
    • 2013-11-06
    • 1970-01-01
    相关资源
    最近更新 更多