是否可以在Cython.pxd上重载cpdef声明?

2024-09-29 18:44:03 发布

您现在位置:Python中文网/ 问答频道 /正文

我在和赛顿捣乱,深入到基维,我一直在尝试我自己的基维财产。在

我有定义DocProperty的以下文件: my.pyx

from kivy.properties cimport Property, PropertyStorage
from kivy._event cimport EventDispatcher

cdef inline void observable_object_dispatch(object self, str name):
    cdef Property prop = self.prop
    prop.dispatch(self.obj, name)


class ObservableObject(object):

    # Internal class to observe changes inside a native python object.
    def __init__(self, *largs):
        self.prop = largs[0]
        self.obj = largs[1]
        super(ObservableObject, self).__init__()

    def __setattr__(self, name, value):
        object.__setattr__(self, name, value)
        observable_object_dispatch(self, name)


cdef class DocProperty(Property):

    def __init__(self, defaultvalue=None, rebind=False, **kw):
        self.baseclass = kw.get('baseclass', object)
        super(DocProperty, self).__init__(defaultvalue, **kw)
        self.rebind = rebind

    cpdef link(self, EventDispatcher obj, str name):
        Property.link(self, obj, name)
        cdef PropertyStorage ps = obj.__storage[self._name]
        ps.value = ObservableObject(self, obj, ps.value)

    cdef check(self, EventDispatcher obj, value):
        if Property.check(self, obj, value):
            return True
        if not isinstance(value, object):
            raise ValueError('{}.{} accept only object based on {}'.format(
            obj.__class__.__name__,
            self.name,
            self.baseclass.__name__))

    cpdef dispatch(self, EventDispatcher obj, str name):
        '''Dispatch the value change to all observers.
       .. versionchanged:: 1.1.0
           The method is now accessible from Python.
       This can be used to force the dispatch of the property, even if the
       value didn't change::
           button = Button()
           # get the Property class instance
           prop = button.property('text')
           # dispatch this property on the button instance
           prop.dispatch(button)
       '''
        cdef PropertyStorage ps = obj.__storage[self._name]
        ps.observers.dispatch(obj, ps.value, (name,), None, 0)

my.pxd

^{pr2}$

和一个快速的sript来尝试一下:my.py

# -*- coding: utf-8 -*-

from kivy.event import EventDispatcher
import pyximport
pyximport.install()
from properties import DocProperty

if __name__ == '__main__':

    class ED(EventDispatcher):

        doc = DocProperty()

        def on_doc(self, obj, value):
            print 'printing doc', self.doc

    class DumbObj(object):

        def __init__(self, num):
            self._num = num

        @property
        def num(self):
            return 5

        @num.setter
        def num(self, value):
            self._num = value

    ed = ED()
    ed.doc = DumbObj(3)
    ed.doc.num = 4

当我跑的时候我的.py,我在DocProperty的dispatch方法上得到一个“Signature not compatible with previous declaration”,因为我尝试重写at属性上的声明,以便它可以比原始代码声明多接受一个参数。有没有可能重载pxd上声明的cpdef方法?如果是,我做错了什么?在

编辑: 在@ead's suggestion之后,我尝试在dispatch的声明中用普通的def替换{}语句,一次只在其中一个文件上。但那没有效果。然后,我试图注释掉对dispatch的调用,看看如果它没有编译失败,会发生什么。结果是DocProperty的两个属性(baseclass和bind)在赋值时引发AttributeError。这很奇怪,因为这些都是从Kivy源复制/粘贴的。这意味着我的.pxd文件对我的Cython代码没有任何影响?我试过从计算机移植我的.pxd进入我的.pyx但这也没有产生结果


Tags: nameselfobjobjectvaluedefpropertynum
1条回答
网友
1楼 · 发布于 2024-09-29 18:44:03

我不知道你为什么要做这样的事,我也不确定从长远来看这是明智之举。但这个问题并非没有兴趣(至少是理论上的)。在

method overriding-相同的名称,相同的签名,不同的实现-这在Cython中是可能的,例如:

%%cython
cdef class A:
  cpdef get_number(self):
    return 0

cdef class B(A):
  cpdef get_number(self):#overrides method
    return 1

def print_number(A obj):
    print("My number is", obj.get_number()) #uses C-level dispatch when possible

现在:

^{pr2}$

这些代码行的内容远不止这些:分派不是在Python中进行的,而是在C级上进行的——比Python的分派快得多,但灵活性稍差。在

还有function overloading-相同的名称,不同的签名,两种方法都可以使用-这是not really possible in Python,因此在Cython中也不是这样。但是,与C语言不同,Python允许更改同名方法的签名,以便使用最后一个签名:

>>> class SomeClass:
     def do_something(self):
       pass
     def do_something(self, i):
       pass
>>> SomeClass().do_something(7) #OK
>>> SomeClass().do_something() #Error, argument is needed

您可以使用Cython实现相同的行为,使用def而不是cpdef函数(但是函数不应该再在pxd中声明,尽管您可以在父类中将其保留为cpdef):

%%cython
...
class C(B):
  def get_number(self, m):
    return min(42,m)

现在会导致错误:

>>> print_number(C())
TypeError: get_number() missing 1 required positional argument: 'm'

这一次使用Python分派(here是关于它如何工作的更多信息)并且类层次结构中get_number的“最后一个”定义需要一个未提供的参数,因此出现错误。在


还有一件事:为什么不能更改函数的签名并将其保留为cpdef?在

cpdef函数具有静态类型部分。类A及其所有子类的函数表中都有一个指向get_number(self)实现的指针,该指针的类型为:

PyObject *(*get_number)(struct __pyx_obj_4test_A *, int __pyx_skip_dispatch);

例如,在A的表中:

struct __pyx_vtabstruct_4test_A {
  PyObject *(*get_number)(struct __pyx_obj_4test_A *, int __pyx_skip_dispatch);
};

get_number(self, int m)相对应的类型是:

PyObject *(*get_number)(struct __pyx_obj_4test_B *, int, int __pyx_skip_dispatch);

签名中有一个附加的int,因此它是不同类型的,不能写入__pyx_vtabstruct_4test_A.get_number-在这里,Cython表现为C

另一方面,在同一个结构中不能有两个不同的成员,因此真正的重载(例如C++中的名称转换)是不可能的。在

相关问题 更多 >

    热门问题