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 Edward d'Auvergne on March 22, 2007 - 05:10:
As far as I'm concerned there is absolutely nothing stopping me in
Python from creating a base class with singleton properties and then
subclassing this to create independent singletons that I can use for
different purposes.  An example of a singleton base class and
inheritance could be as follows.  Say I create a base class
SingletonQueue that has advanced queuing properties (say to be thread
safe) together with the properties of being a singleton.  Then I
create ResultsQueue(SingletonQueue) to queue model-free optimisation
results coming in from remote machines on a grid (there is only one
instance of this queue in the program).  I also create
OptimisationQueue(SingletonQueue) which is a queue for each
optimisation to be sent out to a grid computer (again there is only
one instance of this queue in the program).  These two queues benefit
from being thread safe.  Now, what exactly is stopping me from doing
this?  And what is stopping me from using SingletonQueue as a third
singleton object for a completely unrelated purpose?  What if
SingletonQueue was actually named ThreadQueue and was used as a queue
of threads awaiting execution?  I could use all these three queues,
they would be singletons, and their namespaces will never clash!

For discussions about inheritance of singletons, see the Python
cookbook (http://aspn.activestate.com/ASPN/Cookbook/Python).  Examples
include:

http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/102187 (see
the comments section).
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/66531 (see the
comment with the title 'Singletons using new-style classes')
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/113657 (this
singleton 'behaves well with inheritance')

As for instances of base classes, towards the bottom of the Wikipedia
page http://en.wikipedia.org/wiki/Inheritance_(computer_science) there
is the text:

"In most quarters, class inheritance for the sole purpose of code
re-use has fallen out of favor. The primary concern is that
implementation inheritance does not provide any assurance of
polymorphic substitutability—an instance of the re-using class cannot
necessarily be substituted for an instance of the inherited class. An
alternative technique, delegation, requires more programming effort
but avoids the substitutability issue."

According to this statement 'an instance of the re-using class' is
different to the 'instance of the inherited class'.  The rest of this
text is opinion.  Now according to
http://en.wikipedia.org/wiki/Instantiation_(computer_science):

"Instantiation is the process of creating a specific object which is a
member or instance of a class."

Hence the instance is a specific object.  As the specific object lies
at a specific point in memory space, then if two variables point to
the same instance then the Python the 'id()' function will return the
same number.  According to that function's docstring:

Help on built-in function id in module __builtin__:

id(...)
   id(object) -> integer

   Return the identity of an object.  This is guaranteed to be unique among
   simultaneously existing objects.  (Hint: it's the object's memory address.)

To demonstrate this, try importing the 'singleton_inheritance' module
attached to my message at
https://mail.gna.org/public/relax-devel/2007-03/msg00077.html (the
text at the very bottom) and running the id() functions on the two
singletons:

relax> import singleton_inheritance

Singleton is NewSingleton: False
Contents of Singleton:    ['__doc__', '__init__', '__module__', 'a',
'base_fn', 'x']
Contents of NewSingleton: ['__doc__', '__init__', '__module__', 'a',
'base_fn', 'new_fn', 'x', 'y']
Singleton.base_fn is NewSingleton.base_fn: False

Singleton.a:    1
NewSingleton.a: -1
Singleton.x:    2
NewSingleton.x: 3

Singleton.base_fn():
Hello
NewSingleton.base_fn():
Hello
NewSingleton.new_fn():
Bye
relax> id(singleton_inheritance.Singleton)
1080385452
relax> id(singleton_inheritance.NewSingleton)
1083092716

These are two different memory addresses.  Hence these are two
different objects.  They are also 'simultaneously existing objects'.
As an instance is a specific object, and here with have two specific
objects, we have two instances.  QID!

Bye,

Edward


On 3/22/07, Chris MacRaild <c.a.macraild@xxxxxxxxxxx> wrote:
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 13:00:46 2007