4

I have a cdef class in cython and I want to initialize its fields with the setattr builtin function. However, when I do that I got an execution error :

/path/.../cimul.cpython-34m.so in cimul.Simulation.__init__ (cimul.c:5100)()
AttributeError: 'Simulation' object has no attribute 'Re'

My code is as follow:

cdef class Simulation:
    cdef double Re, Pr, Ra, a, dt_security
    cdef int Nz, NFourier, freq_output, freq_critical_Ra, maxiter
    cdef bool verbose

    def __init__(self, *args, **kargs):
        param_list = {'Re': 1, 'Pr': 1, 'Ra': 1, 'a' : 1, 'Nz': 100,
                      'NFourier': 50, 'dt_security': 0.9,
                      'maxiter': 100, 'freq_output': 10,
                      'freq_critical_Ra':50, 'verbose': False}
        # save the default parameters
        for param, value in param_list.items():
            setattr(self, param, value)

Do you have any idea of how I could circumvent this problem?

cphyc
  • 440
  • 5
  • 16
  • 2
    If you want the attributes to be accessible at Python level, don't you need `cdef public` instead of just `cdef`? That's what it looks like from the tutorial at least, down at the bottom: http://docs.cython.org/src/tutorial/cdef_classes.html – user2357112 Jun 02 '14 at 02:51
  • I edited my code so that it is more "obvious". The error I get at execution time tells my that the attribute `Re` doesn't exist. However, as my example shows it, it is supposed to be created via `setattr`. The error is happening when I try to access my variable inside the class itself (and not from a python frame). – cphyc Jun 02 '14 at 03:13
  • `setattr` is Python-level. It doesn't see the attribute. As far as it knows, your object doesn't support that attribute, and that's the message you get when you try to set an unsupported attribute. – user2357112 Jun 02 '14 at 03:19
  • Ok. How could I do it then? I know I could give an explicit list of variables as the __init__ arguments, but I'm wondering if I can do it the way I'm doing right now using setattr. – cphyc Jun 02 '14 at 03:37
  • 4
    If I'm reading the tutorial correctly, `cdef public` the attributes instead of `cdef`ing them. – user2357112 Jun 02 '14 at 03:43
  • It solved the problem! Thanks. However, it would very nice to have a cython equivalent to setattr to avoid any overhead caused by using cdef public. – cphyc Jun 02 '14 at 04:01
  • If you want to set an attribute on the Cython level without the Python overhead, you will first have to share your extension type properly. See [The documentation](http://docs.cython.org/src/userguide/sharing_declarations.html). Once you've done that, you should be able to do something like `thing.Re = 1.234` to set the attribute. – IanH Jun 02 '14 at 18:12

1 Answers1

2
  • When cdefining (without public) attribute in a class, what you are really doing is defining some field in a C structure. Therefore after the compilation (Cython + C), the name of the attribute are lost, they are only identified by some offset from the beginning of a C struct. A as consequence, they are not accessible from Python.

  • If you add cdef public, they Cython add some property access function which not only allows access from Python but also keep the association identifier <--> offset in C struct. There is a slight overhead going through those property functions. Note also that those function perform a Python -> C type check / conversion.

Now to answer your question, you need somehow to keep the association ident <--> offset. If you want thing to be fast, the only way is to do it by hand:

self.RE = param_list['RE']   # self.RE is a C struct access
self.Pr = param_list['Pr']
...
hivert
  • 10,579
  • 3
  • 31
  • 56