Instances behaviour

M

Mr.Rech

Hi all,
I've been using Python for 3 years, but I've rarely used its OOP
features (I'm a physicist, sorry). Now, after having read a lot about
Python OOP capabilities, I'm trying to get advantage of this (for me)
new paradigm. As a result I've a lot of somewhat philosophical
questions. I will start with one of them.

Suppose I have a bunch of classes that represent slightly (but
conceptually) different object. The instances of each class must behave
in very similar manner, so that I've created a common class ancestor
(let say A) that define a lot of special method (such as __getattr__,
__setattr__, __len__ and so on), and then I've created all my "real"
classes inheriting from it:
..... # here define all special and some common methods
..... # this is the first "real" class
..... # and this is the second

and so on. The problem I'm worried about is that an unaware user may
create an instance of "A" supposing that it has any real use, while it
is only a sort of prototype. However, I can't see (from my limited
point of view) any other way to rearrange things and still get a
similar behaviour.

Implementing those special methods directly in class B and then inherit
from it, doesn't seem the right way, since I'd prefer that instances of
B weren't in any relation with the instances of C (i.e. I'd prefer not
to subclass C from B)

Perhaps some OOP techniques (that I miss at the moment) could be of any
help. Any suggestion?

Thanks in advance,
Andrea.
 
I

Inyeol Lee

]
Suppose I have a bunch of classes that represent slightly (but
conceptually) different object. The instances of each class must behave
in very similar manner, so that I've created a common class ancestor
(let say A) that define a lot of special method (such as __getattr__,
__setattr__, __len__ and so on), and then I've created all my "real"
classes inheriting from it:

.... # here define all special and some common methods

.... # this is the first "real" class

.... # and this is the second

and so on. The problem I'm worried about is that an unaware user may
create an instance of "A" supposing that it has any real use, while it
is only a sort of prototype. However, I can't see (from my limited
point of view) any other way to rearrange things and still get a
similar behaviour.
.... pass
.... .... def __init__(self, foo, bar):
.... A.__init__(self, foo)
.... self.bar = bar
.... Traceback (most recent call last):
File "<stdin>", line 1, in ?

HTH
--Inyeol Lee
 
M

Mike Meyer

Mr.Rech said:
Suppose I have a bunch of classes that represent slightly (but
conceptually) different object. The instances of each class must behave
in very similar manner, so that I've created a common class ancestor
(let say A) that define a lot of special method (such as __getattr__,
__setattr__, __len__ and so on), and then I've created all my "real"
classes inheriting from it:

and so on. The problem I'm worried about is that an unaware user may
create an instance of "A" supposing that it has any real use, while it
is only a sort of prototype. However, I can't see (from my limited
point of view) any other way to rearrange things and still get a
similar behaviour.

Perhaps some OOP techniques (that I miss at the moment) could be of any
help. Any suggestion?

I assume there are methods of B & C that aren't shared, and hence
aren't in A. When the user invokes those, they should get an error
message. That's how this kind of thing is normally dealt with.

If you want things to happen at instantiation time, then you can make
A.__init__ raise an exception. Your B & C __init__ then can't invoke
it. If A.__init__ has a real use, move that into another method that B
& C's __init__ can invoke.

<mike
 
P

Peter Otten

Mr.Rech said:
Suppose I have a bunch of classes that represent slightly (but
conceptually) different object. The instances of each class must behave
in very similar manner, so that I've created a common class ancestor
(let say A) that define a lot of special method (such as __getattr__,
__setattr__, __len__ and so on), and then I've created all my "real"
classes inheriting from it:

....    # and this is the second

and so on. The problem I'm worried about is that an unaware user may
create an instance of "A" supposing that it has any real use, while it
is only a sort of prototype. However, I can't see (from my limited
point of view) any other way to rearrange things and still get a
similar behaviour.

Implementing those special methods directly in class B and then inherit
from it, doesn't seem the right way, since I'd prefer that instances of
B weren't in any relation with the instances of C (i.e. I'd prefer not
to subclass C from B)

Perhaps some OOP techniques (that I miss at the moment) could be of any
help. Any suggestion?

How about

class A(object):
"""Provides common functionality for A-like classes, e. g. B and C.

Do not instantiate.
"""

This is definitely a low-tech approach, but I suppose you don't clutter your
functions with spurious argument type checks, either.
An exception like the one shown by Inyeol Lee is typically raised once
during the development process while my approach bites (only) the
illiterate programmer with a message like
Traceback (most recent call last):
File "<stdin>", line 1, in ?
AttributeError: 'A' object has no attribute 'foo'

which normally can be tracked down almost as quickly -- and which serves him
well anyway :)

Peter
 
B

bruno at modulix

Inyeol Lee wrote:
(snip)


s/TypeError/NotImplementedError/
s/base class/abstract class/
 
M

Mr.Rech

Thanks for your suggestions. They are very usefull and indeed bypass my
problem. However, I've found a (perhaps) more elegant way to get the
same result using metaclasses. My idea is to define my classes as
follows:
..... def __new__(cls, classname, bases, classdict):
# define here all special methods
newdict = { #special methods dict}
classdict.update(newdict) # any suggestion on
automatically build newdict?
return type.__new__(cls, classname, bases, classdict)
__metaclass__ = meta_A
# More methods here

I know metaclasses are a complete different beast, anyway I find this
approach more pythonic. Any comment? Suggestion?

Thanks,
Andrea.
 
P

Peter Otten

Mr.Rech said:
Thanks for your suggestions. They are very usefull and indeed bypass my
problem. However, I've found a (perhaps) more elegant way to get the
same result using metaclasses. My idea is to define my classes as
follows:

.... def __new__(cls, classname, bases, classdict):
# define here all special methods
newdict = { #special methods dict}
classdict.update(newdict) # any suggestion on
automatically build newdict?
return type.__new__(cls, classname, bases, classdict)

Are you intentionally defeating inheritance?
__metaclass__ = meta_A
# More methods here

I know metaclasses are a complete different beast, anyway I find this
approach more pythonic. Any comment? Suggestion?

Godawful.

Don't use classes when functions suffice.
Don't use inheritance when duck-typing suffices.
Don't use metaclasses when inheritance suffices.
Corollary: don't use metaclasses to solve problems you have just made up.

In short, the simplest solution is most likely the most pythonic, even when
some odd corner cases are not covered.

Simplicity also has a nice side effect: fewer bugs.

Peter
 
B

bruno at modulix

Mr.Rech said:
Thanks for your suggestions. They are very usefull and indeed bypass my
problem. However, I've found a (perhaps) more elegant way to get the
same result using metaclasses.


(snip code)
I know metaclasses are a complete different beast, anyway I find this
approach more pythonic.

It's not. It's a case of ArbitraryOvercomplexification(tm).

The pythonic way is to use inheritence and make the base class abstract
by raising a NotImplementedError in it's __init__ (cf Inyeol Lee's
answer and my small correction)
 
M

Mr.Rech

I see your point. Looking again at my metaclass implementation and
comparing it with your abstract class + inheritance approach it turns
out that the latter is definetively more straightforward, easier to
maintain and all in all more pythonic.

Sorry, but being an OOP newbie put me in the position of
overcomplexifing(tm) things a little bit. I'll be back soon with other
(I hope less silly) questions. ;-p

Thanks for all your suggestions,
Andrea
 
I

Inyeol Lee

Inyeol Lee wrote:
(snip)



s/TypeError/NotImplementedError/
s/base class/abstract class/

I prefer TypeError here, NotImplementedError would be OK though.
Here is an example from sets.py in stdlib.


class BaseSet(object):
"""Common base class for mutable and immutable sets."""

__slots__ = ['_data']

# Constructor

def __init__(self):
"""This is an abstract class."""
# Don't call this from a concrete subclass!
if self.__class__ is BaseSet:
raise TypeError, ("BaseSet is an abstract class. "
"Use Set or ImmutableSet.")


Inyeol
 
G

Giovanni Bajo

Mr.Rech said:
and so on. The problem I'm worried about is that an unaware user may
create an instance of "A" supposing that it has any real use, while it
is only a sort of prototype. However, I can't see (from my limited
point of view) any other way to rearrange things and still get a
similar behaviour.


1) Document your class is not intended for public use.
2) Make your A class "private" of the module that defines it. A simple way is
putting an underscore in front of its name.
3) Make your A class non-functional. I assume B and C have methods that A
doesn't. Then, add those methods to A too, but not implement them:

def foo(self):
"""Foo this and that. Must be implemented in subclasses."""
raise NotImplementedError
 

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,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top