boost python and metaclass inheritance
Hi, Does anyone know how to use __metaclass__ specification alongside inheritance from boost python classes? This code illustrates the problem: import abc
from scitbx.lstbx import normal_eqns
class Refinery(object):
__metaclass__ = abc.ABCMeta pass
class AdaptLstbx(Refinery, normal_eqns.non_linear_ls):
pass
The result is Traceback (most recent call last):
File "<stdin>", line 1, in <module> File "/usr/lib/python2.7/abc.py", line 87, in __new__ cls = super(ABCMeta, mcls).__new__(mcls, name, bases, namespace) TypeError: Error when calling the metaclass bases metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
The real Refinery is a class that has one method that must be supplied by a concrete subclass. I like the idea of making it impossible to instantiate a base Refinery rather than relying on e.g. raise NotImplementedError() in that method definition. This is what ABCMeta would give me, if only I could get it to work here. Google found me some answers that require me to know the __metaclass__ of the bases (as suggested by the error message), but normal_eqns.non_linear_ls.__metaclass__ does not exist. I don't terribly mind the raise NotImplementedError() alternative, but I thought that if I can easily use ABCMeta, I'd like to. Cheers -- David
Hi David, I am afraid this is a dead end. Indeed scitbx.lstbx.normal_eqns.non_linear_ls is a Boost Python wrapper. What you could do instead is write: class Refinery(object): def __new__(cls, *args, **kwds): assert cls.__bases__ return object.__new__(cls) class AdaptLstbx(Refinery, normal_eqns.non_linear_ls): pass It is then impossible to construct an instance of Refinery. Only heirs of Refinery can be instantiated. You could make the assert more subtle to suit your needs of course. Generally speaking, i.e. it would not apply to you, David, if you do not plan to commit metaclass-based code to the cctbx, I would strongly advise cctbx developers against using metaclasses at all. This is one of the surest way to make the code impossible to understand to your fellow programmers as it introduces highly implicit behaviour: explicit is better than implicit in the long run. As any rule, it has exceptions: there are 4 uses of metaclasses in the cctbx as I write this. But they are highly specialised and self-contained uses. Best wishes, Luc J. Bourhis
Hi,
Does anyone know how to use __metaclass__ specification alongside inheritance from boost python classes? This code illustrates the problem:
import abc from scitbx.lstbx import normal_eqns
class Refinery(object): __metaclass__ = abc.ABCMeta pass
class AdaptLstbx(Refinery, normal_eqns.non_linear_ls): pass
The result is
Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/usr/lib/python2.7/abc.py", line 87, in __new__ cls = super(ABCMeta, mcls).__new__(mcls, name, bases, namespace) TypeError: Error when calling the metaclass bases metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
The real Refinery is a class that has one method that must be supplied by a concrete subclass. I like the idea of making it impossible to instantiate a base Refinery rather than relying on e.g. raise NotImplementedError() in that method definition. This is what ABCMeta would give me, if only I could get it to work here. Google found me some answers that require me to know the __metaclass__ of the bases (as suggested by the error message), but normal_eqns.non_linear_ls.__metaclass__ does not exist.
I don't terribly mind the raise NotImplementedError() alternative, but I thought that if I can easily use ABCMeta, I'd like to.
Cheers
-- David _______________________________________________ cctbxbb mailing list [email protected] http://phenix-online.org/mailman/listinfo/cctbxbb
Hi Luc,
Thanks for this. I thought that a limited use of python's own abc module
would be one of the safer uses of metaclasses. But apparently even this is
not without pain. My google-fu is slowly warming up this new year, and
eventually I did find a message (from Ralf) suggesting that a boost python
wrapper's __metaclass__ is its __class__, depending how it is constructed.
I still couldn't work out from that how to proceed though, so I'm happy to
put this aside. Your explicit alternative produces exactly the behaviour I
was after, in a more obvious way.
Cheers
-- David
On 8 January 2014 10:05, Luc Bourhis
Hi David,
I am afraid this is a dead end. Indeed scitbx.lstbx.normal_eqns.non_linear_ls is a Boost Python wrapper. What you could do instead is write:
class Refinery(object):
def __new__(cls, *args, **kwds): assert cls.__bases__ return object.__new__(cls)
class AdaptLstbx(Refinery, normal_eqns.non_linear_ls): pass
It is then impossible to construct an instance of Refinery. Only heirs of Refinery can be instantiated. You could make the assert more subtle to suit your needs of course.
Generally speaking, i.e. it would not apply to you, David, if you do not plan to commit metaclass-based code to the cctbx, I would strongly advise cctbx developers against using metaclasses at all. This is one of the surest way to make the code impossible to understand to your fellow programmers as it introduces highly implicit behaviour: explicit is better than implicit in the long run.
As any rule, it has exceptions: there are 4 uses of metaclasses in the cctbx as I write this. But they are highly specialised and self-contained uses.
Best wishes,
Luc J. Bourhis
Hi,
Does anyone know how to use __metaclass__ specification alongside inheritance from boost python classes? This code illustrates the problem:
import abc from scitbx.lstbx import normal_eqns
class Refinery(object): __metaclass__ = abc.ABCMeta pass
class AdaptLstbx(Refinery, normal_eqns.non_linear_ls): pass
The result is
Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/usr/lib/python2.7/abc.py", line 87, in __new__ cls = super(ABCMeta, mcls).__new__(mcls, name, bases, namespace) TypeError: Error when calling the metaclass bases metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
The real Refinery is a class that has one method that must be supplied by a concrete subclass. I like the idea of making it impossible to instantiate a base Refinery rather than relying on e.g. raise NotImplementedError() in that method definition. This is what ABCMeta would give me, if only I could get it to work here. Google found me some answers that require me to know the __metaclass__ of the bases (as suggested by the error message), but normal_eqns.non_linear_ls.__metaclass__ does not exist.
I don't terribly mind the raise NotImplementedError() alternative, but I thought that if I can easily use ABCMeta, I'd like to.
Cheers
-- David _______________________________________________ cctbxbb mailing list [email protected] http://phenix-online.org/mailman/listinfo/cctbxbb
_______________________________________________ cctbxbb mailing list [email protected] http://phenix-online.org/mailman/listinfo/cctbxbb
a boost python wrapper's __metaclass__ is its __class__, depending how it is constructed. I still couldn't work out from that how to proceed though,
Yes, indeed, I forgot about that. So here is the solution then, but I still recommend to forget about it. import abc from scitbx.lstbx import normal_eqns class Refinery(object): class __metaclass__(normal_eqns.non_linear_ls.__class__, abc.ABCMeta): pass class AdaptLstbx(Refinery, normal_eqns.non_linear_ls): pass Best wishes, Luc
import abc from scitbx.lstbx import normal_eqns class Refinery(object): class __metaclass__(normal_eqns.non_linear_ls.__class__, abc.ABCMeta): pass
class AdaptLstbx(Refinery, normal_eqns.non_linear_ls): pass
Except that it does not prevent r = Refinery() after all since the __metaclass__ has a valid instantiation through normal_eqns.non_linear_ls.__class__. which gives me a flashback: I've already burnt myself with that! Luc
Hi Luc,
Actually it almost works, because Refinery should not inherit
from normal_eqns.non_linear_ls, only the adapter, AdaptLstbx. So I can do:
import abc
from scitbx.lstbx import normal_eqns
class Refinery(object):
__metaclass__ = abc.ABCMeta
pass
class AdaptLstbx(Refinery, normal_eqns.non_linear_ls):
class __metaclass__(normal_eqns.non_linear_ls.__class__, abc.ABCMeta):
pass
pass
This successfully stops Refinery being constructed. However, you do point
out the critical flaw, which is that AdaptLstbx can now be constructed, yet
this still lacks the missing 'run' method, which would be supplied another
child. Now the original raise NotImplementedError() seems like the clearest
solution. I suppose this is why people on the internet argue that abc isn't
pythonic.
Cheers
-- David
On 9 January 2014 00:48, Luc Bourhis
import abc from scitbx.lstbx import normal_eqns class Refinery(object): class __metaclass__(normal_eqns.non_linear_ls.__class__, abc.ABCMeta): pass
class AdaptLstbx(Refinery, normal_eqns.non_linear_ls): pass
Except that it does not prevent r = Refinery() after all since the __metaclass__ has a valid instantiation through normal_eqns.non_linear_ls.__class__.
which gives me a flashback: I've already burnt myself with that!
Luc
_______________________________________________ cctbxbb mailing list [email protected] http://phenix-online.org/mailman/listinfo/cctbxbb
participants (2)
-
David Waterman
-
Luc Bourhis