cython's property and nondata descriptors

301 views
Skip to first unread message

Nils Bruin

unread,
May 16, 2013, 1:18:47 PM5/16/13
to cython-users
Two questions about cython's "property:" directive.

1] In python, a nondata descriptor is a descriptor that doesn't have
its '__set__' slot filled. Such descriptors can be overridden by
entries in the instance '__dict__'. One might expect that specifying a
cython "property" without giving a "__set__" method would lead to such
an entry, but it doesn't: It leads to a datadescriptor that cannot be
overridden by instance attributes:

%cython
cdef class T(object):
property B:
def __get__(self):
return 1


>>> class S(T): pass
>>> s=S()
>>> s.B=10
AttributeError: attribute 'B' of 'T' objects is not writable

This mirrors the @property decorator in python but it makes it hard to
specify nondata descriptors. Are there thoughts about how to make it
more convenient to declare nondata descriptors

2] Using the @property decorator in a cdef class works, but leads to
less efficient code:

%cython
cdef class T(object):
property B:
def __get__(self):
return 1
@property
def C(self):
return 1

>>> t=T()
>>> timeit("t.B")
625 loops, best of 3: 78.6 ns per loop
>>> timeit("t.C")
625 loops, best of 3: 275 ns per loop

would it be desirable to automatically transcribe "@property" to
"property .."? Perhaps not, since the semantics of the two are
different and both are valid, but I expect it's a common pitfall for
people who try to translate python to cython code.

Nikita Nemkin

unread,
May 16, 2013, 1:27:56 PM5/16/13
to cython...@googlegroups.com
On Thu, 16 May 2013 23:18:47 +0600, Nils Bruin <bruin...@gmail.com>
wrote:

> 1] In python, a nondata descriptor is a descriptor that doesn't have
> its '__set__' slot filled. Such descriptors can be overridden by
> entries in the instance '__dict__'. One might expect that specifying a
> cython "property" without giving a "__set__" method would lead to such
> an entry, but it doesn't: It leads to a datadescriptor that cannot be
> overridden by instance attributes:
>
> %cython
> cdef class T(object):
> property B:
> def __get__(self):
> return 1
>
>
>>>> class S(T): pass
>>>> s=S()
>>>> s.B=10
> AttributeError: attribute 'B' of 'T' objects is not writable
>
> This mirrors the @property decorator in python but it makes it hard to
> specify nondata descriptors. Are there thoughts about how to make it
> more convenient to declare nondata descriptors

Cython does create nondata descriptor in this example.
The error occurs for a completely different reason: cdef classes don't
have a __dict__ slot, see http://trac.cython.org/cython_trac/ticket/745.

AFAIK, you can only get __dict__ by subclassing, like:

cdef class _T: ... # no __dict__, fast property access
class T(_T): ... # has __dict__ and all properties from _T


Best regards,
Nikita Nemkin

Nils Bruin

unread,
May 16, 2013, 1:43:03 PM5/16/13
to cython-users
On May 16, 10:27 am, "Nikita Nemkin" <nik...@nemkin.ru> wrote:

> Cython does create nondata descriptor in this example.
> The error occurs for a completely different reason: cdef classes don't
> have a __dict__ slot, seehttp://trac.cython.org/cython_trac/ticket/745.
>
> AFAIK, you can only get __dict__ by subclassing, like:
>
>      cdef class _T: ...  # no __dict__, fast property access
>      class T(_T): ...    # has __dict__ and all properties from _T

Yes, that's why I subclassed S from T above. With that I get:
>>> s=S()
>>> s.D
AttributeError: 'S' object has no attribute 'D'
>>> s.D=10
>>> s.D
10
>>> s.B
>>> s.B=10
attribute 'B' of 'T' objects is not writable
>>> s.__dict__['B']=10
>>> s.B
B
>>> print s.__dict__
{'B': 10, 'D': 10}

so s.B seems like a data descriptor to me.

Nils Bruin

unread,
May 16, 2013, 1:44:44 PM5/16/13
to cython-users
On May 16, 10:43 am, Nils Bruin <bruin.n...@gmail.com> wrote:
> >>> s.B
> B

Sorry that should be

>>> s.B
1

it give the value back from the datadescriptor inherited from T.

Nikita Nemkin

unread,
May 16, 2013, 2:21:34 PM5/16/13
to cython...@googlegroups.com
On Thu, 16 May 2013 23:43:03 +0600, Nils Bruin <bruin...@gmail.com>
wrote:
You are right, I am wrong.
Descriptors created through PyGetSetDef are always data descriptors,
which to me looks like an oversight in C API.

Cython could probably define its own data descriptor type
that mimics getset_descriptor but has empty tp_descr_set.


Best regards,
Nikita Nemkin
Reply all
Reply to author
Forward
0 new messages