我正在搞乱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)
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,但这并没有产生结果
答案 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)。