From the Cython docs:
The __cinit__() method is where you should perform basic C-level initialisation of the object, including allocation of any C data structures that your object will own. You need to be careful what you do in the __cinit__() method, because the object may not yet be a fully valid Python object when it is called. Therefore, you should be careful invoking any Python operations which might touch the object; in particular, its methods and anything that could be overridden by subtypes (and thus depend on their subtype state being initialised already).
By the time your __cinit__() method is called, memory has been allocated for the object and any C attributes it has have been initialised to 0 or null. (Any Python attributes have also been initialised to None, but you probably shouldn’t rely on that.) Your __cinit__() method is guaranteed to be called exactly once.
The docs then go on for several more paragraphs defining just what the limitations are. There may be some obscure reason to use this, but __init__ is efficient enough and it's not worth it especially since zero initialization is fine. Also one of the important restrictions here is that you can't access other Python objects and this has to inherit from Python-provided base classes, plus calling into Synthizer itself is in no way basic c-level initialization. Also whatever overhead might exist here is immeasurably small as compared to creating the Synthizer-side objects anyway and you don't get out of a __init__ for most of it because the user needs to be able to provide parameters and expects docstrings in the right places.
If it's not going to be zero initialized then it might error, which means needing to throw an exception, and nothing about these docs is suggesting that that's a safe thing to do either.
I can't use char * which is native in C because in Python 3 str is unicode and Synthizer uses UTF8, so conversion is necessary. I could use the raw Python types if I wanted to put in a lot of extra effort for absolutely no gain whatsoever other than making everyone's lives difficult, and also Synthizer is char * and wants UTF8, not UTF16, since everyone who makes a halfway sane C abstraction that has to deal with Unicode doesn't change their basic char type for Windows. I could use Py_UNICODE* but that doesn't solve the problem of needing to accept or hand out str objects to Python code outside the bindings in addition to changing the underlying character type, which would have to be something Synthizer then exposed in the public API just so that Python could use it.
I don't like shutting people down and I understand that you're trying to help, but please stop trying to help. I've tried to be really patient with you in general, but this is continuing a pattern from the other thread that I don't have the bandwidth for where I have to explain the things you're trying to explain to me and why I'm not using them and/or why they don't work. If you find a bug I'm happy to address it, but I don't have the bandwidth to address code reviews in general, and I especially don't have the bandwidth to address code reviews from people who clearly only have a theoretical knowledge of any of the topics at hand. And even if I did, I have neither the bandwidth nor the interest of optimizing the Cython bindings to be some version of perfect. The overhead isn't in Python, I promise you that, and the only reason I'm even bothering with Cython rather than making my life super easy and using CFFI is that it opens up some options for later around custom generators and byte streams that I won't even be touching for months.