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

时间:2018-06-18 19:07:02

标签: python override kivy cython

我正在搞乱Cython,深入了解kivy,我一直在尝试制作自己的Kivy Property。

我有以下文件来区分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

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

cdef class DocProperty(Property):
    cdef object baseclass
    cdef public int rebind
    cpdef dispatch(self, EventDispatcher obj, str name)

快速尝试一下: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

当我运行my.py时,我在DocProperty的调度方法上得到一个'Signature与先前声明不兼容',因为我尝试在Property上覆盖它的声明,因此它可以接受一个比原始代码声明更多的参数。是否有可能重载在pxd上声明的cpdef方法?如果是这样,我做错了什么?

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

1 个答案:

答案 0 :(得分:0)

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

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

"n"

现在:

labels_type <- paste(ifelse(grepl("mystring", temp_labels), "n", "g"), collapse = "")

这些代码行还有很多东西,而不是眼睛:发送不是在Python中发生,而是在C级发生 - 比Python的发送速度快得多,但灵活性稍差。

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

%%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

您可以使用>>> print_number(A()) 0 >>> print_number(B()) 1 代替>>> 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中声明,尽管您可以将其保留为{{1}在父类中):

cpdef

现在导致错误:

pxd

这次使用Python-dispatch(here是关于它如何工作的更多信息)和&#34; last&#34;类层次结构中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' 函数具有静态类型的部分。类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) - 此处Cython的行为为C.

另一方面,在一个具有相同名称的结构中不能有两个不同的成员,因此不可能实现真正的重载(例如在C ++中使用其namemangling)。