-
Notifications
You must be signed in to change notification settings - Fork 118
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
HocObject.__new__
requires constructor arguments, HocObject.__init__
should do that.
#1129
Comments
Would it make sense to repair the new/init pairs in nrnpy_hoc.cpp and nrnpy_nrn.cpp to conform to standard python policy? |
* added pyenv's .python-version to gitignore * prevent import py2/3 module in py3/2. removed `exec` wrapper from hclass * Changed comment and removed non-existing `nonlocal_hclass` import * Removed traling whitespaces and double newline at eof * Removed the MetaHocObject metaclass and added `nonlocal_hclass` factory * New `HocBaseObject`; (nonlocal_)hclass uses this new base. * Added dev note to demystify quintessential link between HOC & Python * moved __all__ to PEP8 location * Fixed error with grandchildren of `HocBaseObject` * Apply changes only to Python 3.6+ where metaclass alternative is availbl * fixed import typo * hclass of hoc_type should precede use of hoc.HocObject API's * Fixed usage of module_name * added hclass35.py to Makefile, excessive trailing newlines. * `module` does not exist, see details on mixed approaches in PR#1096 * black formatting & updated hclass2 derived docstrings * Store dummy modules on `neuron` module to better emulate submodules * Add a TypeError if init but not new has been given (see #1129) * Added _pyobj_enabled flag to check whether Python object interface is up * Explicitly defined __new__ for 3.6+ pyobj compatibility * Class composition is allowed if HOC types are the same * added tests for HOC type Python classes, test only in Python 3.6+ * Removed 'super()' call for Python 2 * Revert "added hclass35.py to Makefile, excessive trailing newlines." This reverts commit 080f14a. * Added hclass35 to Makefile * Revert "Removed traling whitespaces and double newline at eof" This reverts commit 046fab4. * Reinstated deprecation messages * Merged hclass and nonlocal_hclass
* added pyenv's .python-version to gitignore * prevent import py2/3 module in py3/2. removed `exec` wrapper from hclass * Changed comment and removed non-existing `nonlocal_hclass` import * Removed traling whitespaces and double newline at eof * Removed the MetaHocObject metaclass and added `nonlocal_hclass` factory * New `HocBaseObject`; (nonlocal_)hclass uses this new base. * Added dev note to demystify quintessential link between HOC & Python * moved __all__ to PEP8 location * Fixed error with grandchildren of `HocBaseObject` * Apply changes only to Python 3.6+ where metaclass alternative is availbl * fixed import typo * hclass of hoc_type should precede use of hoc.HocObject API's * Fixed usage of module_name * added hclass35.py to Makefile, excessive trailing newlines. * `module` does not exist, see details on mixed approaches in PR#1096 * black formatting & updated hclass2 derived docstrings * Store dummy modules on `neuron` module to better emulate submodules * Add a TypeError if init but not new has been given (see #1129) * Added _pyobj_enabled flag to check whether Python object interface is up * Explicitly defined __new__ for 3.6+ pyobj compatibility * Class composition is allowed if HOC types are the same * added tests for HOC type Python classes, test only in Python 3.6+ * Removed 'super()' call for Python 2 * Revert "added hclass35.py to Makefile, excessive trailing newlines." This reverts commit 080f14a. * Added hclass35 to Makefile * Revert "Removed traling whitespaces and double newline at eof" This reverts commit 046fab4. * Reinstated deprecation messages * Merged hclass and nonlocal_hclass
* added pyenv's .python-version to gitignore * prevent import py2/3 module in py3/2. removed `exec` wrapper from hclass * Changed comment and removed non-existing `nonlocal_hclass` import * Removed traling whitespaces and double newline at eof * Removed the MetaHocObject metaclass and added `nonlocal_hclass` factory * New `HocBaseObject`; (nonlocal_)hclass uses this new base. * Added dev note to demystify quintessential link between HOC & Python * moved __all__ to PEP8 location * Fixed error with grandchildren of `HocBaseObject` * Apply changes only to Python 3.6+ where metaclass alternative is availbl * fixed import typo * hclass of hoc_type should precede use of hoc.HocObject API's * Fixed usage of module_name * added hclass35.py to Makefile, excessive trailing newlines. * `module` does not exist, see details on mixed approaches in PR#1096 * black formatting & updated hclass2 derived docstrings * Store dummy modules on `neuron` module to better emulate submodules * Add a TypeError if init but not new has been given (see #1129) * Added _pyobj_enabled flag to check whether Python object interface is up * Explicitly defined __new__ for 3.6+ pyobj compatibility * Class composition is allowed if HOC types are the same * added tests for HOC type Python classes, test only in Python 3.6+ * Removed 'super()' call for Python 2 * Revert "added hclass35.py to Makefile, excessive trailing newlines." This reverts commit 080f14a. * Added hclass35 to Makefile * Revert "Removed traling whitespaces and double newline at eof" This reverts commit 046fab4. * Reinstated deprecation messages * Merged hclass and nonlocal_hclass
Hi @nrnhines I'm not familiar with the CPP part of NEURON so I can't really answer that. In Python I would solve it by implementing a class BaseHocObject(HocObject):
def __init_subclass__(cls, hoctype=None, **kwargs):
super().__init_subclass__(**kwargs)
def call(*args, **kwargs):
# Call the hoc object factory without args
instance = cls.__new__(hoctype)
# Pass the args to init
instance.__init__(*args, **kwargs)
# Check that the user called `super().__init__()` with the expected args with a flag
if not getattr(instance, "_hoc_init_passed", False):
raise Exception("Python HOC objects must call `super().__init__()`)
cls.__call__ = call This way HOC can always instantiate the right data structure without any args, and by setting a flag on the object we can confirm that the object has been properly initialised! So:
|
I don't have a good enough understanding of python capi new/init implementation standards to safely refactor the existing version. I do remember being quite puzzled about when init was actually used. For example, presently, PyObject* NPySecObj_new is itself |
The basic
It needn't be: An unitialized section isn't a valid Section, that's entirely correct to note that. But it isn't It's definitely not the way it usually goes in OO languages, but it is what it is. If |
Thanks. That's very helpful. Is the properly initialised flag something automatic? I see that
|
No, it's a manual addition of this check I proposed: if not getattr(instance, "_hoc_init_passed", False):
raise Exception("Python HOC objects must call `super().__init__()`) we'd set that on all of the HocObjects whose class My100BigVector(HocObject, hocbase=h.Vector):
def __init__(self, *args):
super().__init__(100)
self.args = args because |
The reason why both init and new are called here (if it were pure Python objects, the C thing makes me unsure):
is because the return value of
😉 The metaclass of
with a regular Python class:
|
Hah, so:
do you see how the Now the problem is actually restricted to
new should only care about the |
For what it's worth, according to https://llllllllll.github.io/c-extension-tutorial/appendix.html#c.PyTypeObject.tp_new
|
I guess that pretty much settles the abstract question. The decision then, in each case, is whether the object is mutable or immutable. |
Well in Python mutability is only considered in as far as immutability is an obstacle people run into and try to work around ;) If an object's attributes can be changed after construction it is mutable. The immutable aspect of a Section pointing to a NEURON section is exactly why |
In Python
__new__
and__init__
have 2 distinct purposes:__new__
creates an instance, usually of the same type as the type object and returns it, while__init__
initializes instances of the type object, this means that__new__
should only use the arguments it is given to help it determine the type of the instance to create, but should not use them to initialize the created object.HocObject.__new__
however does jump the gun there: it creates an instance of the type object (determined by thehocbase
kwarg) but also requires the constructor arguments of the HOC type and uses them to construct a fully initialized object.This leads to a problem in inheritance where a class that inherits from
h.List
cannot rewrite its__init__
arguments to be anything other than the arguments expected byh.List
, since the arguments are first passed to__new__
and need to match the argumentsHocObject.__new__
require to construct ah.List
object: theh.List
arguments, QED.The only way around this problem is for child classes that implement
__init__
to also implement__new__
and to rewrite the args they pass intosuper().__new__
.The solution should be that
__new__
only does a placeholder construction of the correct type, but waits for the arguments inHocObject.__init__
before finishing construction of the object.The text was updated successfully, but these errors were encountered: