help needed with classes/inheritance

B

barbaros

Hello everybody,

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.

Thank you. Cristian Barbarosie
http://cmaf.fc.ul.pt/~barbaros
 
B

Bruno Desthuilliers

barbaros a écrit :
Hello everybody,

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:

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

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).
 
A

Andrew Lee

barbaros said:
Hello everybody,

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
-------------------------------------------

class Body(object) :
...

class OrientedBody (Body):
def __init__(self, orientation = 1) :
Body.__init__(self)
self.orientation = orientation



as noted


But, also.

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.

If I write --

class foo (object):
def __init__ :
pass

def some_func (self, val) :
if isinstance (val, "bar") :
....

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.


-- Andrew
 
B

Bruno Desthuilliers

Andrew Lee a écrit :
(snip)
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.
Nope.

Remember, every class ought to have a well defined internal state and a
well defined interface to its state.

I don't see how the part about the internal state relates to the problem
here.

If I write --

class foo (object):
def __init__ :
pass

def some_func (self, val) :
if isinstance (val, "bar") :
....

Then I am either doing something very wrong

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).

my 2 cents.
 
B

barbaros

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...

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.

Thank you. Cristian Barbarosie
http://cmaf.fc.ul.pt/~barbaros
 
P

Paul McGuire

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
 
B

Bruno Desthuilliers

barbaros a écrit :
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)
 
B

barbaros

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.

Thank you very much. Cristian Barbarosie
http://cmaf.ptmat.fc.ul.pt/~barbaros
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,483
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top