Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Do Cython extension types support class attributes?

Python classes can have class attributes:

class Foo(object):
   bar = 4

Is there an analogous construct for defining class attributes in Cython extension types? For example, when I try to compile the following cython code

cdef class Foo:
    cdef int bar
    bar = 4

I get this error:

thing.c:773:3: error: use of undeclared identifier 'bar'
  bar = 4;
  ^
1 error generated.
error: command 'cc' failed with exit status 1
like image 280
Pwnosaurus Avatar asked Feb 26 '15 17:02

Pwnosaurus


People also ask

What is Cython file extension?

Cython files have a .pyx extension. At its most basic, Cython code looks exactly like Python code. However, whereas standard Python is dynamically typed, in Cython, types can optionally be provided, allowing for improved performance, allowing loops to be converted into C loops where possible.

Is Cython object oriented?

Cython is fast at the same time provides flexibility of being object-oriented, functional, and dynamic programming language. One of the key aspects of Cython include optional static type declarations which comes out of the box.

Does Cython have pointers?

The Cython language uses the normal C syntax for C types, including pointers.

Does Python have classes?

Python classes provide all the standard features of Object Oriented Programming: the class inheritance mechanism allows multiple base classes, a derived class can override any methods of its base class or classes, and a method can call the method of a base class with the same name.


2 Answers

The short answer is yes and no.

No, there is not a convenient syntactic idiom for quickly inserting a class attribute in a cdef class. However ....

The whole point of cython is that it gives you lower level access. The usual motive for the extra effort is performance, but you can also do C-like things with the extra freedom. The difficulty is, there are many pitfalls, and in this case, you won't get pure python class attributes without a lot of work. It is nevertheless pretty easy to get what you need for simple use cases.

For example, suppose I'm making some calculating engine as a class and I wish to globally set the precision of the return value for all instances. I want a default at compile time, and from time to time I may want to adjust it lower to quickly process some trials, and then adjust it higher for my final work. A class attribute is made to order, but you can get the functionality you need in cython as follows:

First, define at the module level the following:

cdef int _precision[1]  # storage for my class 'attribute'
_precision[0]=8         # my default value, set during compilation

Using an array permits us to use the cython idiom precision[0] which is equivalent to the C *precision. The cdef name precision is implicitly a pointer since the data item is an array. This permits using cython syntactic idioms to convert from cython storage locations to python references. If all you want is a global constant that may be accessed by cdef code in any of the classes in the module, you are done. If you want to use it strictly as a class attribute, you must enforce that discipline - the compiler doesn't care.

Now if you also want to adjust the value from python code, you will need a pair of cdef functions that python code in the module can call to access the 'attribute':

cdef int*  get_precision(): return _precision
cdef void* set_precision(int i): _precision[0]=i

At this point, the semantics will vary a bit from pure python, unless you really want to sweat. You need a python setter and getter function, and I find the python descriptor protocol implemented by properties is easiest:

cdef class SomeCalculator:
  ...

  property precision:
    def __get__(self):
      """Get or set calculation precision, default == 8.
         This is like a class attribute: setting affects all instances,
         however, it also affects all subclasses."""
      return get_precision()[0]
    def __set__(self,int integer): set_precision(min(30,max(0,integer)))

The first gets a python reference to the 'attribute'. The second sets the 'attribute' with a python integral value, policed to fall within limits. The cython function call and return interface automatically takes care of conversions, which are more complex than they look.

For example, get_precision returns a C-pointer. If you did the dereferencing in get_precision you would get an error trying to return a C-int in __get__ as if it were python. If instead you just omitted the [0] dereference in __get__ you would get an error trying to return a C-pointer as if it were a python int. As written, automatic conversions correctly match types. cython is very finicky about this sort of thing, and can silently return incorrect values, discoverable only at runtime. It can take some experimentation to infer the correct incantation.

The docstring tells you not to expect a pure python class attribute. If you want to sub-class, and have sub-classes use a different global setting, you will need to sweat a bit more. In python, all that is done automatically.

Even so, there are other differences. A real class attribute may be referenced on the class or on an instance. This property may only be referenced on an instance. Setting a real class attribute on the instance creates an instance specific copy, leaving the class attribute untouched, but invisible to the altered instance.

For the given use case, this works. A real class attribute is unnecessary. Since cython code is usually less abstract and compute intensive, this minimal approach is often enough.

like image 96
took_awhile_to_get_this Avatar answered Oct 12 '22 23:10

took_awhile_to_get_this


While it doesn't seem to be possible to have C-typed static attributes, Cython extension types can have regular Python static attributes which are also automatically accessible in Python. Just declare them as you would declare them in Python:

cdef class Foo:
    bar = 4

The generated code shows that these static attributes are stored as Python objects in the attribute dict of the class object, i.e. if you use them in contexts where C-types are used, they are converted back from Python objects.

like image 37
michitux Avatar answered Oct 12 '22 23:10

michitux