如何在运行时选择性地启用cdef类的任意属性赋值

时间:2018-01-24 23:10:07

标签: cython

假设我有cdef class

cdef class MyClass:
    # declare some cdef attributes here
    cdef dict __dict__
    def __cinit__(self, *args, **kwargs):
        self.__dict__ = {}
        # do some other init stuff

我通常希望能够向MyClass个实例添加属性,因此我有一个__dict__属性。

我还希望有一种方法可以在运行时关闭它。我尝试添加__setattr__

    def __setattr__(self, attr, val):
        if not _STRICT:
            object.__setattr__(self, attr, val)
            return

        if hasattr(self, attr):
            object.__setattr__(self, attr, val)
        else:
            raise ValueError(
                "Cannot set attribute '" + attr + "' on " + 
                self.__class__.__name__ +
                ' because that attribute does not exist!'
            )

其中_STRICT是一个可以在运行时切换的变量。 这个技巧在纯python中运行得很好,但是使用扩展类型我收到了这个错误:

TypeError: can't apply this __setattr__ to MyClass object

docs似乎表示我应该能够制作自定义__setattr__,但我似乎遇到了this更改,导致无法使用object.__setattr__在内置类型上设置属性。我尝试使用super().__setattr__,但错误的方式完全相同。

是否有其他方法可以设置我缺少的Cython扩展类型的属性,还是有其他方法可以做到这一点?

1 个答案:

答案 0 :(得分:1)

好的,我通过直接调用C api超越了我referenced的块:

from cpython.object cimport PyObject_GenericSetAttr

...

cdef class MyClass:

    ...

    def __setattr__(self, attr, val):
        # in non-strict mode, we always update the attribute or
        # add it to the instance even if it is not already present
        if not _STRICT:
            PyObject_GenericSetAttr(self, attr, val)
            return

        # If we got here, we are in strict mode, and we only
        # allow attribute setting if the instance already has
        # that attribute
        if hasattr(self, attr):
            PyObject_GenericSetAttr(self, attr, val)
        else:
            raise ValueError(
                "Cannot set attribute '" + attr + "' on " + 
                self.__class__.__name__ +
                ' because that attribute does not exist!'
            )

注意:我尝试解决此问题的方法之一就是按照评论中的建议更新__dict__。这不起作用,因为cdef属性未存储在实例__dict__中。如果您使用__dict__[attr] = val更新dict,它会很乐意将其添加到dict中,但如果使用getattr(instance, attr)关键字声明attr,则cdef将不会返回您的更新。