New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Multiple inheritance #1927
Multiple inheritance #1927
Conversation
796d58d
to
354d713
Compare
354d713
to
c2e7b11
Compare
@@ -4709,6 +4676,31 @@ def analyse_declarations(self, env): | |||
else: | |||
scope.implemented = 1 | |||
|
|||
if len(self.bases.args) > 1: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like you changed the indentation space in a couple of places (4->2). I would prefer sticking with the standard of 4 spaces everywhere.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not intentionally, thanks for catching.
Cython/Compiler/Nodes.py
Outdated
type = entry.type | ||
typeobj_cname = type.typeobj_cname | ||
scope = type.scope | ||
if scope: # could be None if there was an error |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know, the code was like this before, so this comment is really misplaced here (sorry), but now that this old code is touched anyway, let me say that it would be a good opportunity to clean up this kind of useless level of indentation. If the code handles an error case, it should just say so and bail out early, instead of using lengthy indented if-blocks without an else. I don't mind long functions at all, as long as their structure is simple. But indented conditional blocks increase the complexity because if I can't see where an if-block ends, I have to assume that there is an else-case (and mentally prepare for it). If there is no else-case after a long block, then the if-block probably shouldn't be there in the first place, but be replaced by a short error handling case of the reversed condition, or be moved into a separate method entirely.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure.
Cython/Compiler/Nodes.py
Outdated
code.error_goto(entry.pos))) | ||
if heap_type_bases: | ||
code.putln("#if PY_VERSION_HEX >= 0x03050000") | ||
code.putln("%s.tp_flags &= ~Py_TPFLAGS_HEAPTYPE;" % typeobj_cname) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm. Eventually, we will have to switch to heap-types anyway to properly support PEP 489, submodules, reloading, etc. I understand that this doesn't have to happen right now, but you would probably agree that this flag unsetting seems like a fairly crude hack.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, this is an ugly hack (and one of my hesitations for the whole PR) but I couldn't figure out a way around it without making everything heap allocated (a much bigger change, though, as you said, we may need to go there).
Cython/Compiler/Nodes.py
Outdated
from . import ExprNodes | ||
self.type_init_args = ExprNodes.TupleNode( | ||
self.pos, | ||
args=[ExprNodes.StringNode(self.pos, value=self.class_name), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IdentifierStringNode
is better here.
Cython/Compiler/Nodes.py
Outdated
base_class_scope = None | ||
elif not base_type.is_extension_type and \ | ||
not (base_type.is_builtin_type and base_type.objstruct_cname): | ||
print (base_type, base_type.is_builtin_type) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
;)
Cython/Compiler/Nodes.py
Outdated
base_type = base.analyse_as_type(env) | ||
if base_type in (PyrexTypes.c_int_type, PyrexTypes.c_long_type, PyrexTypes.c_float_type): | ||
# Use the Python rather than C variant of these types. | ||
base_type = env.lookup(str(base_type)).type |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd rather call sign_and_name()
here instead of relying on the output of str()
.
Nice feature! |
We should be doing this for pure Python defined classe as well, but don't have a way to inject it.
Hmm, I think this might have an impact on the https://github.com/cython/cython/blob/0.27.3/Cython/Compiler/ModuleNode.py#L1807 We should now also look into the Python base classes for special methods, and (I think) also generally delegate missing special methods to a runtime lookup. |
Good point, these special methods that delegate to other (sets of) special methods need more sophisticated handling. Seems this may also be an issue for Python classes that inherit from cdef classes with this kind of special method. |
Original change cython#1927
Are there any plans for multiple extension inheritance (cdef class)? f |
I wouldn't think so. It's a limitation of how Python handles extension types (i.e. any type defined as a C struct). Try |
I am a bit unfamiliar with the lower level internals of python but that would also imply that multi-level inheritance is out for extensions written in cython? I currently this problem with a 'need' for a third layer of class inheritance. According to the docs this is fine for normal python objects but the not for extensions, I was wondering why. |
The basic problem for multiple inheritance is that an extension type gets translated into:
In order to function properly you really need exactly one Single inheritance is fairly simple:
For multiple inheritance though it's impossible to include extension types in Multi-level inheritance is no problem and has worked for a long long time. You can have as many "layers" of inheritance as you like (so
This fulfils all the requirements: one However, if you have questions about a specific implementation problem then you should follow it up on the cython-users mailing list rather than this PR. |
Just to clarify, you The third point only diverges slightly from Python's own behaviour. You cannot create a Python class that inherits from two builtin/extension types with different object layouts (e.g. This restriction could be lifted, but it's probably less interesting than the change in this PR. Note that Python classes are not "slow" or "bad" per se. If you think that you have a specific need for inheriting from more than one cdef class which cannot (more or less) easily be resolved by using (compiled) Python classes, then I would suggest opening a new feature ticket that describes your use case and the reasons why a mix of cdef and Python classes cannot work here. If it's unclear for you how to handle a specific use case with the current feature set, then I would second @da-woods' suggestion of asking on the cython-users mailing list for help. |
See https://bugs.python.org/issue22079 and the solution in cython from 2017 cython/cython#1927 (review) If we, like CPython turn on CYTHON_USE_TYPE_SPECS then the cython hack goes away
No description provided.