mailRe: Vote for the design of the relax data storage object singleton.


Others Months | Index by Date | Thread Index
>>   [Date Prev] [Date Next] [Thread Prev] [Thread Next]

Header


Content

Posted by Chris MacRaild on March 21, 2007 - 19:50:
On Sat, 2007-03-17 at 12:27 +1100, Edward d'Auvergne wrote:
The attached file is a Python module, try importing it and then
playing with its contents.  The module contains two singletons -
'Singleton' and 'NewSingleton'.  Both are instantiated in the same
manner as new relax data storage object (implementation B which we
voted for), the class name being masked by the instance reference.
NewSingleton is derived from the Singleton base class demonstrating
inheritance.  QID!

These singletons are two completely different objects (in different
memory space) which can be used for completely different tasks and can
be used by completely different parts of the program all while being
true singletons - only one instance of each will ever exist in the
program.  If you cannot see this, I challenge you to write a counter
example module demonstrating that what this extremely basic module is
doing cannot be done. 

What this module is doing can be done (of course), but it breaks the
Singleton design because:

isinstance(NewSingleton, Singleton.__class__)
True

Contrary to your previous assertions, isinstance is aptly named, and
correctly identifies the inheritance relationship between these two
objects. In other words, because NewSingleton inherits from
Singleton.__class__ (ie. the class, not the instance of the same name),
NewSingleton is an instance of Singleton.__class__

This is fundamental to the nature of inheritance, and is the basis of
our dissagreements. Again, the classic inheritance metaphor: class Apple
is a subclass of the class Fruit, and GrannySmith is a subclass of the
Apple. Your argument that an object cannot be an instance of more than
one class flies in the face of reason - quite clearly, any object which
is an instance of the class GrannySmith, is also an instance of Apple
and of Fruit. This is essential to the nature of sub-classing, in both
common logic and in OO programing.

Perhaps it is helpful to consider the distinction between Inheritance
and Delegation. Both achieve similar things in terms of code reuse and
behavioural modification, but they do so in quite different ways, and
have important functional differences. Given some base class with lots
of methods that I want to access, while making a few changes:

class Base:
...     def methodA(self):
...         print "A"
...     def methodB(self):
...         print "B"
...     # lots more methods...

I could inherit from Base, and change the functionality:

class Inherited(Base):
...     def methodA(self):
...         print "a\n"

Or I could delegate to Base those methods that I don't redefine:

class Delegated:
...     def methodA(self):
...         print "a\n"
...     def __getattr__(self, attr):
...         # If attribute lookup fails, delegate to Base
...         return getattr(Base(), attr)

Instances of the two classes, Inherited and Delegated, are functionally
very similar: they have all of the same methods, most of which are
identical to those of Base. There is one important difference:

isinstance(Inherited, Base)
True
isinstance(Delegated, Base)
False

A great example of inheritance in the sense I am describing comes from
the Python exceptions system. All python exceptions are an instance of
the base class Exception, and from there ther exists a hierachy of
exceptions: ArithmeticError is a subclass of Exception, and in turn is a
base class for all of the standard maths errors (OverflowError,
ZeroDivisionError, etc); likewise LookupError derives from Exception,
and is base class for KeyError and IndexError. This hierachy is very
powerful, because it allows us to handle different types of error in
different ways:

try:
...     errorProneFunction(args)
... except LookupError:
...     dealWithLookupError(args)
... except ArithmeticError:
...     dealWithArithmeticError(args)
... except:
...     # All other errors
...     dealWithOtherError(args)

Suppose there is a case that I want to catch as an exception, that is
maths-related. Although it is not one of the standard maths errors, I
want to handle it in the same way (with the function
dealWithArithmeticError() ). I simply need to subclass from
ArithmeticError:

class MyMathsError(ArithmeticError):
...     pass

This works because an exception which is an instance of MyMathsError is
also an instance of ArithmeticError, so the statement except
ArithmeticError will catch MyMathsError, as well as OverflowError,
ZeroDivisionError and the rest. Importantly, no amount of delegation
will make this work:

class AnotherMathsError:
...     def __getattr__(self, attr):
...         # If attribute lookup fails, delegate to Base
...         return getattr(ArithmeticError(), attr)

AnotherMathsError is not caught by except ArithmeticError, because it
lacks the requisite inheritance relationship - it is not an instance of
ArithmeticError.

So, to restate what I think is the orthodox OO understanding of
inheritance: any object which is an instance of a sub-class S is also an
instance of all of the classes which are base-classes of S. This is
stated somewhat obtusely at http://en.wikipedia.org/wiki/Inheritance_%
28computer_science%29 in the second paragraph:

"Inheritance is also sometimes called generalization, because the is-a
relationships represent a hierarchy between classes of objects. For
instance, a "fruit" is a generalization of "apple", "orange", "mango"
and many others. One can consider fruit to be an abstraction of apple,
orange, etc. Conversely, since apples are fruit (i.e. an apple is-a
fruit), apples may naturally inherit all the properties common to all
fruit, such as being a fleshy container for the seed of a plant."

and (similarly obscurely) in almost all basic descriptions of Object
Orientated coding. This is critically important, because it allows
client code to use instances of the sub-class as if they were instances
of the base-class, without knowing anything about the properties (or
even the existance) of the sub-class.

Chris







Related Messages


Powered by MHonArc, Updated Thu Mar 22 05:20:40 2007