mailInheritable singleton python example on Wikipedia.


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

Header


Content

Posted by Edward d'Auvergne on March 23, 2007 - 04:25:
I've just noticed that the Singleton design pattern Wikipedia page
(http://en.wikipedia.org/wiki/Singleton_pattern) was changed on the 29th
of January to include an example of Python code for an inheritable
singleton:

http://en.wikipedia.org/w/index.php?title=Singleton_pattern&diff=104294450&oldid=104070484

Edward




On Thu, 2007-03-22 at 11:39 +0000, Chris MacRaild wrote:
On Thu, 2007-03-22 at 15:09 +1100, Edward d'Auvergne wrote:
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!

Python has never claimed to be an arbiter of logic, or even of good
programming practise - just because Python allows you to do it, it
doesn't mean it makes sense, or that it is a good idea. No-one is
suggesting there is any impediment to what you describe, except for the
fact that SingletonQueue is no longer a singleton, because both the
single instance of ResultsQueue and the single instance of
OptimisationQueue are also instances of SingletonQueue. You have two
instances of the same class, therfore the class is not a singleton.

Here is how to impliment exactly what you describe, without breaking the
singleton design pattern:

class AbstractQueue:
...     # NOTE: this is NOT a singleton!
...     # define a general queue here
class ResultsQueue(AbstractQueue):
...     # modify the default queue for results
class OptimisationQueue(AbstractQueue):
...     # modify the default queue for oplimisations
ResultsQueue = ResultsQueue()
OptimisationQueue = OptimisationQueue()

We have exactly one instance of ResultsQueue and OptimisationQueue, so
both are singletons, as per the design. AbstractQueue never claimed to
be a singleton, because it isn't - ResultsQueue and OptimisationQueue
are both instances of AbstractQueue.


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

Sure, but there are plenty of contradictory statements here too, eg.:

http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/66531
(inheriting singleton vs inheriting borg):
"What happens in a large system when two separate subsystems both
inherit from (extend) a singleton-class? It's anything but clear to me
-- error at runtime, at compiletime, at linktime (none such in Python,
let's say at import-time), or what behavior...?"

http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/465850:
"One problem introduced here is that inheritance is broken, but that is
more of a problem of the Singleton pattern than of this recipe."


The moral: you can find someone on a public message board to support
almost any opinion.




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.

Well, at least you are honest about quoting selectively. Infact
polymorphism can be rather a headache in many languages, but in this
respect Python is much beter than most. Of course 'an instance of the
re-using class' is different to the 'instance of the inherited class',
but the instance of the re-using class is also an instance of the
inherited class. ie. there are two classes, two objects, and three
instance relationships linking the classes with the objects: the one
object is just an instance of the base class, while the other object is
an instance of both the base class and the sub-class.

  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:

One subtle point that might help here - 'instance' refers first to a
relationship, it is only a conventional shorthand that allows us to say
'the instance is an object'. Instance is a non-exclusive relationship
between two objects, so there can be many instance relationships between
only a few objects. This can lead to confusion if we take too litteraly
the 'instance is an object' type shorthand and try to generate a
one-to-one correspondence between instancehood and object. 


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!

QID what? You have convinsingly demonstrated that two different objects
are indeed two different objects. This has no bearing on whether they
share instance relationships with the base class. Indeed the fact that
the are two different objects is exactly why they breach the singleton
design of the base class.


Chris



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 Fri Mar 23 05:20:17 2007