I am building a code for surface meshes (triangulations for instance). I need to implement Body objects (bodies can be points, segments, triangles and so on), then a Mesh will be a collection of bodies, together with their neighbourhood relations. I also need OrientedBody objects, which consist in a body together with a plus or minus sign to describe its orientation. So, basically an OrientedBody is just a Body with an integer label stuck on it.
I implemented it in a very crude manner: ------------------------------------------ class Body: [...] class OrientedBody: def __init__ (self,b,orient=1): # b is an already existing body assert isinstance(b,Body) self.base = b self.orientation = orient -------------------------------------------
My question is: can it be done using inheritance ? I recall that I need three distinct objects: the basic (non-oriented) body, the same body with positive orientation and the same body with negative orientation.
> I am building a code for surface meshes (triangulations for instance). > I need to implement Body objects (bodies can be points, segments, > triangles and so on), then a Mesh will be a collection of bodies, > together with their neighbourhood relations. > I also need OrientedBody objects, which consist in a body > together with a plus or minus sign to describe its orientation. > So, basically an OrientedBody is just a Body with an > integer label stuck on it.
> I implemented it in a very crude manner: > ------------------------------------------ > class Body:
Unless you need compatibility with pre-2.2 Python versions, use a new-style class instead:
class Body(object):
> [...] > class OrientedBody: > def __init__ (self,b,orient=1): > # b is an already existing body > assert isinstance(b,Body) > self.base = b > self.orientation = orient > -------------------------------------------
> My question is: can it be done using inheritance ?
Technically, yes:
class OrientedBody(Body): def __init__(self, orient=1): Body.__init__(self) self.orient = 1
Now if it's the right thing to do is another question... Another possible design could be to have an orient attribute on the Body class itself, with 0 => non-oriented (default), 1 => 'plus', -1 => 'minus' (or any other convention, depending on how you use this attribute).
> I am building a code for surface meshes (triangulations for instance). > I need to implement Body objects (bodies can be points, segments, > triangles and so on), then a Mesh will be a collection of bodies, > together with their neighbourhood relations. > I also need OrientedBody objects, which consist in a body > together with a plus or minus sign to describe its orientation. > So, basically an OrientedBody is just a Body with an > integer label stuck on it.
> I implemented it in a very crude manner: > ------------------------------------------ > class Body: > [...] > class OrientedBody: > def __init__ (self,b,orient=1): > # b is an already existing body > assert isinstance(b,Body) > self.base = b > self.orientation = orient > -------------------------------------------
as a rule of thumb .. if you are using "isinstance" in a class to determine what class a parameter is ... you have broken the OO contract. Remember, every class ought to have a well defined internal state and a well defined interface to its state.
Then I am either doing something very wrong or very clever (either can get me in trouble)
In Python it is preferred that I write two functions some_func_a and some_func_b
e.g.
def some_func_a (self, val = None, class = bar) : assert(isinstance (class, "bar"), True) ....
def some_func_b (self, val = None, class = baz) : assert (isinstance (class, "baz"), True)
C++ and Java try to enforce the OO contract by making data and methods private, protected or public. Which helps -- but leads to some confusion (what is protected inheritance in C++????) Python exposes all of its classes internals to everyone -- but that doesn't mean you should touch them!!
As Larry Wall once wrote, "There is a difference between, 'do not enter my living room because I asked you not to' and 'do not enter my living room because I have a shotgun'"
Python adopts the 'do not touch my private parts because I asked you not to' idiom. (In C++, only friends can touch your privates ... ;-)
So -- be mindful that checking the class of well defined parameters at anytime is breaking the contract -- you may need to do it -- but it is more likely that you aren't adhering to good OOD.
Does that make any sense?
Seriously -- I have not had any coffee yet and I am still new at Python.
> My question is: can it be done using inheritance ? > I recall that I need three distinct objects: > the basic (non-oriented) body, the same body with positive > orientation and the same body with negative orientation.
If you do so in a desperate attempt to emulate static typing in Python, then yes, you're doing something very wrong. Else:
> or very clever
Not necessarily. Given Python's dynamic typing, there's no builtin OneObviousWay(tm) way to dispatch on different functions based on argument's type - something often done in statically typed OOPLs using method overridding (ie: different methods with same name but different signatures). Doing a manual dispatch based on argument's type, while not good OO style, is sometimes the simplest working solution, and a good enough one for the problem at hand. Having different methods for different arg types has the drawback that you don't have one single generic function/method that you can use as a callback.
Having a real multidispatch (or rule-based dispatch etc) system either builtin or in the stdlib would indeed be much cleaner. Until then, there are a couple third-part packages solving this problem, but it can be overkill for simple problems (and add unneeded/unwanted dependencies).
> Now if it's the right thing to do is another question...
If I understand correctly, in the above implementation I cannot define firstly a (non-oriented) body, and then build, on top of it, two bodies with opposite orientations. The point is, I want both oriented bodies to share the same base Body object.
> Another > possible design could be to have an orient attribute on the Body class > itself, with 0 => non-oriented (default), 1 => 'plus', -1 => 'minus' (or > any other convention, depending on how you use this attribute).
The above comments apply here, too.
In what concerns other suggestion, about Python language, I shall do my best to understand them and apply them.
On Apr 23, 10:44 am, barbaros <barba...@ptmat.fc.ul.pt> wrote:
> If I understand correctly, in the above implementation I cannot > define firstly a (non-oriented) body, and then build, on top of it, > two bodies with opposite orientations. The point is, I want > both oriented bodies to share the same base Body object.
What you are describing is composition+delegation, not inheritance, and it would be the same answer in Java, C++, or OO-langue-du-jour. Python makes delegation to the contained object easier than the others (except maybe for OOldj) - no need to implement all the methods of the contained object in the container, that just delegate the call to the contained; use __getattr__ to get attributes of the contained object that are not defined on the container (methods are attributes, too) as shown below. No inheritance in this example at all.
-- Paul
class BodyWithoutOrientation(object): def __init__(self,color): self.color = color def show_orientation(self): print "I don't lean one way or the other"
class OrientedBody(object): def __init__(self,base,orientation): self.base_body = base self.orientation = orientation def show_orientation(self): print "I lean to the " + \ { OrientedBody.RIGHT : "right", OrientedBody.LEFT : "left", }[self.orientation], print "and my color is " + self.color # delegate any other attr lookups to the base_body object def __getattr__(self,attr): return getattr(self.base_body,attr) OrientedBody.RIGHT = object() OrientedBody.LEFT = object()
class LeftRightBody(object): def __init__(self,b): self.common_base = b self.left = OrientedBody(b,OrientedBody.LEFT) self.right = OrientedBody(b,OrientedBody.RIGHT) def show_orientation(self): print "I do both of these:" print "- ", self.left.show_orientation() print "- ", self.right.show_orientation()
base = BodyWithoutOrientation("purple") lr = LeftRightBody(base)
base.show_orientation() lr.show_orientation()
Prints: I don't lean one way or the other I do both of these: - I lean to the left and my color is purple - I lean to the right and my color is purple
>> Now if it's the right thing to do is another question...
> If I understand correctly, in the above implementation I cannot > define firstly a (non-oriented) body, and then build, on top of it, > two bodies with opposite orientations. The point is, I want > both oriented bodies to share the same base Body object.
Then it's not a job for inheritence, but for composition/delegation - Sorry but I didn't get your specs quite right :-/
Now the good news is that Python makes composition/delegation close to a no-brainer:
class Body(object): # definitions here
class OrientedBody(object): # notice that we *dont* inherit from Body def __init__(self, body, orient): self._body = body self.orient = orient
def __getattr__(self, name): try: return getattr(self._body, name) except AttributeError: raise AttributeError( "OrientedBody object has no attribute %s" % name )
def __setattr__(self, name, val): # this one is a bit more tricky, since you have # to know which names are (or should be) bound to # which object. I use a Q&D approach here - which will break # on computed attributes (properties etc) in the Body class - # but you can also define a class attribute in either Body # or OrientedBody to explicitly declare what name goes where if name in self._body.__dict__: setattr(self._body, name, val) else: object.__setattr__(self, name, value)
Thanks to Paul McGuire and Bruno Desthuilliers for their comprehensive answers. This is exactly what I was looking for, except I did not know the correct name (composition/delegation). Now all I have to do is to study the supplied code, understand it and adapt it to my problem.