On Fri, 2007-03-23 at 14:59 +1100, Edward d'Auvergne wrote:
If the base class cannot be a singleton due to the derived class objects being instances of it, then how can the three different values of 'x' below be explained?
Please state specifically what conflict you see between these two ideas, because, as I've said several times already, I can see none.
$ python Python 2.4.1 (#2, Oct 6 2006, 15:14:48) [GCC 4.0.1 (4.0.1-5mdk for Mandriva Linux release 2006.0)] on linux2 Type "help", "copyright", "credits" or "license" for more information.from Queue import Queue class ThreadQueue(Queue, object):... instances = {} ... def __new__(cls, *args, **kargs): ... if ThreadQueue.instances.get(cls) is None: ... ThreadQueue.instances[cls] = object.__new__(cls, *args, **kargs) ... return ThreadQueue.instances[cls] ...class ResultsQueue(ThreadQueue):... mf_results = {} ...class OptimisationQueue(ThreadQueue):... min_instances = [] ...# Create the three singletons.... thread_queue = ThreadQueue()results_queue = ResultsQueue() opt_queue = OptimisationQueue() # Show that the singletons have different locations in memory - i.e.are different instances. ... print id(thread_queue) 1077833228print id(results_queue)1077867628print id(opt_queue)1077881612# Add objects to the singletons.... thread_queue.x = 1results_queue.x = 2 opt_queue.x = 3 # Print the objects of the singletons.... print thread_queue.x 1print results_queue.x2print opt_queue.x3
So, at this point we have three classes: ThreadQueue, ResultsQueue and OptimisationQueue. We have three objects, which are class instances: thread_queue, results_queue and opt_queue. We are in full agreement that these are three different objects, so there is no suprise that you can create an attribute of the same name in each object, and bind that attribute to different values. Attributes of different objects are themselves different objects, and there is no common namespace, so all three attributes can have the same name without conflict. All of this has nothing to do with the fact that results_queue has instancehood relationships both with ResultsQueue and with ThreadQueue, and opt_queue has has instancehood relationships both with ResultsQueue and with OptimisationQueue.
print results_queue is thread_queueFalseprint opt_queue is thread_queueFalseprint results_queue is opt_queueFalse# Instantiate the three singletons again.
This is a contradiction in terms! If they are singletons, you instatiate once and once only. Any attempt to do otherwise should either raise an exception, or simply return the single existing instance of the class. You have coded the second option, so ...
... thread_queue2 = ThreadQueue()results_queue2 = ResultsQueue() opt_queue2 = OptimisationQueue()
You have simply rebound thread_queue as thread_queue2, results_queue as results_queue2 and opt_queue as opt_queue2. So...
# Print the objects of the singletons.... print thread_queue2.x 1print results_queue2.x2print opt_queue2.x3
This follows obviously:
thread_queue is thread_queue2
True
results_queue is results_queue2
True
opt_queue is opt_queue2
so:
thread_queue.x is thread_queue2.x
True etc. Again, where is the conflict? Chris
Bye, 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 hereclass ResultsQueue(AbstractQueue):... # modify the default queue for resultsclass OptimisationQueue(AbstractQueue):... # modify the default queue for oplimisationsResultsQueue = 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. ChrisBye, 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)Trueisinstance(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