Five Easy Pieces: Simple Python Non-Patterns

Alex Martelli, AB Strakt

Abstract

Design Patterns, and full fledged Pattern Languages, were originally introduced by C. Alexander as ways to reason about the architecture of building and cities, [al79]. More recently, Design Patterns have become deservedly popular in software development. However, not all design issues are so hard as to really need the power of these semi-formal approaches. When one is addressing simple issues, using very powerful approaches may be overkill, a bit like the "Big Design Up Front" fallacy [bd00] so prevalent in software development. Python tends to make many issues simpler. This, in turn, sometimes lets you use simpler tools, e.g. an idiom in lieu of a Design Pattern. Simplicity is an important ingredient of quality. This paper argues for this perspective, and exemplifies it with five simple Python "solution elements" that are not Design Patterns but may, to some extent, be satisfactorily used instead of Design Patterns in various contexts.

Patterns and non-Patterns

"Design Patterns" (henceforth DPs) have been brewing for quite a while. From the point of view of most software practitioners, however, DPs burst suddenly upon the scene when E. Gamma, R. Helm, R. Johnson and J. Vlissides (henceforth the "Gang of Four", or Gof4 — other works also call them the GoF, or GOF) published their book "Design Patterns" [go95]. Overnight, and deservedly, this book made DPs popular in the software development community.

The Patterns community has produced a lot of other fascinating publications connected with software development, particularly on Pattern Languages (integrated, coordinated systems of Patterns). These works deal with every conceivable area of software development, including organizational structure, analysis, and development processes, as well as design. A complete collection of all fundamental books and articles on these themes would overflow the average developer's available shelf space. Still, any developer should at least be familiar with the original Gof4's DP book, M. Fowler's "Analysis Patterns" [fo97] and "Refactoring" [fo99], and Vlissides' "Hatching Patterns" [vl98]. These are all very practical, highly usable books, directly oriented to software development. I think it's also a good idea to read some wider-view, "philosophical reflection" works in the Pattern field. My personal "dark horse" suggestion for the latter category is N. Salingaros' paper "The Structure of Pattern Languages" [sa99], which focuses on Alexander's original work on Pattern Languages for building and city architecture, [al77].

But I come neither to praise Design Patterns, nor, most particularly, to bury them: just to offer a small alternate perspective of their use, and non-use, when one is developing Python programs. A more traditional approach, centered on implementing Gof4's DPs rather than looking for alternatives, is competently explored in [sa98].

I focus on contrast (or, actually, just nuance!) from the Gof4's DP book. Indeed, by its superb quality and for being in the right place at the right time, the Gof4's book is the best known work on Design Patterns among the software community. The Gof4 do make the point, right in the first chapter, that "the choice of programming language is important because it influences one's point of view". The Gof4 consider this influence strong enough that language choice shapes what is or isn't "worthy" of being framed as a DP, or even feasible to so frame. Many of the book's patterns are of course C++ specific, at least to the extent of making most sense in a C++-like context (access specifiers, compile-time type checking, etc).

More to the point, however, a DP is, conceptually, a somewhat "heavy" construct. This, of course, applies in spades to a complete Pattern Language. But even just to write down a DP requires a semi-formal approach. You must identify relevant object and classes, at the right levels of abstraction. Then, you must single out and articulate intent, motivation, applicability, structure, participants, collaboration, consequences, implementation. Most particularly, you must research, find, and document Known Uses. Known Uses are not an optional element of a DP: at the very least, by the Gof4's relatively benign criterion, it's not a Pattern unless you can identify at least two usage examples from different domains. Writing down the DP implies a substantial investment of effort, so you need the "known uses" to provide some assurance that the whole conceptual edifice is indeed worthy of that effort, having proven itself in the field. This is just fine and dandy when this considerable investment of time and energy pays back by helping us control and conquer complexity, which is often the case. But what if some of the complexity goes away, or at least is considerably diminished, by the programming language we're using?

Python's forte is simplicity. Again and again, problems that are difficult in other languages scale down to "pretty easy" in the light of Python's bright Sun. The amount and caliber of intellectual weaponry that it makes sense to bring to bear on a problem depends on the problem's difficulty level. For a simpler problem, an informal approach may make more sense, while a harder problem might profit from more structured and formalized procedures. One plus of making problems simpler is that, the simpler a problem, the more simplicity you can deploy in solving it. Maybe you can get away with an idiom (language specific usage), or even just "good common practice", where another language would require you to unearth and apply a full-fledged design pattern. At times, it's a substantial simplicity gain to avoid forcing an aspect of the solution into the object oriented mold, so pervasive, and indeed so often appropriate, in software development Design Patterns. One aspect of Python's simplicity is, indeed, that you only do OO when you want to.

Borg versus Singleton

A popular, often implemented Gof4 DP is Singleton. One can't help but wonder why: besides the catchy name, what does Singleton offer that makes it so appealing? The DP's stated Intent is to "ensure a class only has one instance" (and provide a global point of access to it). But why would we want to ensure that? Aren't we entering in the middle of the action, where a specific approach has already been chosen to solve some other actual underlying problem, just as in Bentley's classic "How do I use the system sort from within my program" question (in the very first essay of his outstanding "Programming Pearls" [be00])?

I think we are. Coming to the Motivation, in fact, we find that "It's important for some classes to have exactly one instance. Although there can be many printers in a system, there should be only one printer spooler". And so, maybe, there should be, depending on the system that we're modeling. But this still doesn't tell us why that "one printer spooler" in the system we're modeling should necessarily, or optimally, correspond to "exactly one instance" of some specific class in the software we write to model that system.

There appears to be a lot of unspoken subtext in this intent and motivation. It is taken for granted, I surmise, that instance identity must, or at least should, be in direct correspondence with an "entity in the real world": more precisely, with a conceptual entity in the world view that our software system is modeling. Some schools of OO design take this as an axiom, but the Gof4 argue directly against this earlier in the book. In section 1.6 in the Gof4 book, we read: "object oriented designs often end up with classes that have no counterpart in the real world ... abstractions that emerge during design are key to making a design flexible". Right on! I happen to agree very closely with this opinion. Yet, in identifying Singleton's intent and motivation, the need for "counterparts", which section 1.6 in the book denies, seems to be an unspoken assumption.

Object identity is a frail reed on which to rely. A. Korzybski, engineer extraordinaire, had choice words on the subject of "the is of identity" (a catchy yet precise phrase he credits to mathematician W. S. Jevons) in [ko33]. Less catchily, but just as precisely, G. Santayana had remarked in [sa23] that "Whenever I use the word 'is', except in sheer tautology, I deeply misuse it". L. von Wittgenstein, another contemporary engineer of notice, had similarly observed in [wi22] that "to say of two things that they are identical is nonsense, and to say of one thing that it is identical with itself is to say nothing at all". (Why do we, collectively, keep forgetting the key insights of our grandparents' generation? Could it be that people who do not know history are doomed to repeat it? Korzybski's best known quote seems to be directly on the issue of "counterparts in the real world" for objects in an OO design: "the map is not the territory"!)

What's in an instance

Lest I be accused of escaping into philosophy, although I am quoting mostly engineers, let's look more closely at that "instance" thing, of which Singleton wants to ensure a certain class has only one. In Python, an instance has an identity: we know because we can print id(instance) and see that identity displayed as an integer right in front of our eyes. An instance has state: assuming the instance belongs to a "classic class" (an assumption that we'll have to critically revisit later, of course), the instance's state, sticking for a moment to its "direct", per-instance part, is entirely held in instance.__dict__. An instance has behavior, coded in its methods, which, in most cases, come (some have argued, should always come) as the methods of instance.__class__ — some of them, of course, could in turn be inherited, but that doesn't affect the point. That's it — that's all an instance has: identity, state, behavior.

Of course, an instance's "state" in a wider sense may partly come from elsewhere. instance.__class__.__dict__ is a popular place in which to keep some state, specifically that part which is shared by all instances of the class. Then, there are global variables, in the dictionary of the class's module, and, potentially, even more indirect repositories of state. However, as we're discussing Singleton, we need not dwell on all of these potential depots of state, as they're obviously held in common by all instances of a class, and possibly by other entities yet. If there is any motivation for ensuring that a class is only ever instantiated once, the motivation cannot lie in aspects in which other instances of the same class, if they existed, would be identical and indistinguishable. The hypothetical motivation we're seeking has to rest, if anywhere, then in those aspects that might distinguish, i.e., differentiate, a multiplicity of instances, were such multiplicity allowed.

By the same token, the hypothetical advantage of Singleton cannot be about behavior, i.e., code. Normally, all instances of a class share behavior. We can arrange for deviations from this rule, by binding freshly created bound methods as part of per instance state, but it's quite a moot point whether we ever should. Again, therefore, behavior-wise it cannot matter much whether we constrain a class to have just one instance: even if the class has several instances, they share behavior, as long as we conventionally agree to eschew the dubious practice of creating and binding new per instance bound methods, at least regarding instances of that particular class. Let's not forget that Python gains much of its ease and simplicity by substituting clear, sensible conventions in place of the hard, strictly-enforced rules which many other languages try to impose on programmers.

So, it boils down to identity, and per-instance state. Why would we want to ensure uniqueness of identity? If it's for the purpose of testing equality with is, why not define __eq__ instead? That gives us at least as much control. If it's for the purpose of using the instance as a dictionary key, without forcing the instance to be immutable, we can get there just as well by defining __hash__: the "immutability" only needs to refer to equality comparison results being unchangeable, and hash(instance) being similarly fixed. We don't need to get metaphysical about this, but, should we want to, it's easy to argue that "immutability" is defined by the context: if we ensure that the code of built-in type dict, the only relevant "observer", can never notice any mutation, then who's to say any mutation has in fact occurred...? So, naah, we don't really care about identity, not deeply at all. So, is it all about per instance state...?

I think it must be. When we say there's only one printer spooler, all we really care about is that there be only one "printer spooler state": just one set of queues, jobs in the queues, currently set options, and so forth. Now this is concrete and juicy enough to sink our teeth into. Don't we need to ensure there is only one instance of the class, so that there will only be one "occurrence" of the relevant state?

Borg: just share state!

And the answer is, no, of course we don't. Not in any language, or object model, actually. We just have to ensure that all instances, whatever number thereof might be in existence, share state. This can be accomplished in any language, typically by delegation in some form. Once we do that, we satisfy the real application need that may indeed arise: uniqueness of state. Ensuring that the "number of instances" is identically equal to 1 is one approach, but it's far from being the only viable one.

So, we need to weigh the actual advantages and disadvantages of the Singleton approach versus delegation based alternatives. In the alternatives, we allow client code to instantiate the target class freely, but we must arrange for all instances thus created to delegate their state to a single agent. You could look at these alternatives as Structural patterns, or even, by a bit of a stretch, Behavioral ones, while Singleton is a Creational pattern. For example, it's easy to envision the connection, almost antisymmetric, between shared-state multiple instances and the Design Pattern Flyweight. Some of the Applicability issues are very close: state is made extrinsic, the application must not depend on object identity.

Before we proceed further, we really need a catchy name. Naming is important, and the Patterns community recognizes this in earnest. Singleton thrives, "out there" in the real world of software development, in part because its name is so catchy. A good name makes something easier to recall, recognize, discuss. What name would well represent some sort-of-Flyweight objects, numerous as to identity, but deeper down all the same, indistinguishable, because they all share state? Well, what about Borg? After all, the several instances, despite their distinct identities, are really all One, because they have no distinct state. Identity is irrelevant, resistance is futile, prepare to be assimilated...! Yep, it works (and I am indebted to D. Ascher for suggesting this name in discussion on the Python Cookbook site, [ma01a]). Some might object to naming software artifacts by inspiration from popular television shows, but surely no such objection will come from a programming community which centers on a language whose name honors Monty Python. Indeed, we might prefer to rename Singleton to Highlander, since "There Can Be Only One"...

So, how hard is it to implement Borg? Not very, in any language or object model I know of: at worst, one has to explicitly code some delegation boilerplate, but often it's not even as bad as that. For example, in Consequences n. 5 of Singleton, the Gof4 claim Singleton is "More flexible than class operations", i.e., use of "static" member functions in C++, for example because those can't be overridable (virtual, in C++ terms). True, but so what? Who'd ever want to use static methods? All we need is for the instance methods, which can perfectly well be virtual ones in C++, to only use "static" — i.e., per-class, rather than per-instance — member data. Prepend keyword static to all data members, leave the methods per-instance, virtual if you need or want them that way, and bingo, instant C++ Borg. This was discussed as the Monostate Pattern in [wh96].

Borg in Python

But, of course, it is even easier in Python (isn't it always?-):

class Borg:
    _shared_state = {}
    def __init__(self):
        self.__dict__ = self._shared_state
That's it: just derive your application class from Borg, mixin-like. Remember, of course, to invoke Borg.__init__(self), right at the start of your own __init__ if any, like for any other Python inheritance. Once you do this, your class is a Borg: all instances of your class share state. Your class may in turn override Borg's _shared_state class attribute. It is exactly in order to allow this "data override" that Borg.__init__ accesses the attribute through self, not directly by qualifying Borg, and the attribute's name has one leading underscore, not two. This data-member overriding, or lack thereof, determines whether your class also shares state with other subclasses of Borg: you can easily arrange this in different ways, but, of course, resistance is futile, so don't even bother trying to arrange that.

Let's take a small step back to look at this tiny snippet of code with "beginners' eyes". Our design intent is for all instances to share state. Our Python knowledge tells us that each instance's state lives in the instance's __dict__, ignoring, without loss of generality, other state that is already shared "by nature", and delaying for the moment the issue of Python 2.2's non-classic classes. Therefore, we explicitly express this design intent by ensuring that the __dict__ is the same dictionary object for each and every instance, as elementarily done by our assignment. This is totally consequential, direct, even trivially obvious. I shamefully confess it took me a while of fiddling with __getattr__, __setattr__ and __delattr__, before the obvious solution at last jumped out at me. We can be so conditioned to complexity and cleverness, that it becomes hard to find the simplicity and obviousness "hiding" right in front of our nose! Fortunately, Python helps a lot in the quest for simplicity and clarity. Indeed, I think this obvious, elementary use of self.__dict__ re-binding, to express an important design intent in a direct, elementary way, validates Guido's then-controversial choice, back in the pre-historical times when he was designing Python 1.5.2, to allow this re-binding. Pity that non-classic classes, in Python 2.2, lose this ability; we'll see later that this is not fatal, but it does make us work harder.

Back to what Borg is giving us... Client code can just instantiate your Borg derived class, just as it might instantiate any other class. Borg is not a Creational pattern: as already mentioned, it verges more on the Structural, although you could make a case about it having Behavioral aspects. This means that Borg carries no Creational constraints. To put it another way, Borg does not require Creational Collaborations from client code. Like any other Python class meant to be inherited, Borg does of course require from subclasses the elementary Collaboration of calling the superclass's __init__.

Borg is a simple idea: it does not conflate different and unrelated concerns, nor does it attempt to solve other possible problems, not directly related to "state sharing" (or "limiting number of instances"). If you want to have "controlled access to instances", for example, which is listed as benefit number 1 for the Gof4's Singleton Pattern, you have to deal with it in some other way. Just as you would for any other class for which you deem controlled access desirable, whatever the number of its instances and the kind of state these instances hold. Controlled access is clearly an orthogonal consideration, independent from "limiting number of instances", or "state sharing".

Ability to subclass class Singleton is very important in the Singleton DP: it's the second condition in Applicability, and the key issue in Consequences n.3 and n.5. However, the issue of choosing which of the Singleton's subclasses is actually instantiated looms large. This issue takes up two thirds of the Implementation section, leading to a rich, complex solution, a registry of singletons. Even this rich solution still doesn't meet many typical application needs. What if two separate subsystems each need to refine Singleton by subclassing? Instances of the two separate subclasses can't both exist, or else two separate "instances of Singleton" would exist, each as a sub-object (base object) of one of the subclasses. Borg has no problem with this, of course: as many instances as needed exist, all sharing state, and therefore, in particular, any subclass of Borg may be independently further subclassed as needed. Often, the independent subclasses are each providing different behavior tweaks or additions with different mixins. State is still shared, but of course each independent subclass may easily avoid accidental interference with another, in the usual Python way, i.e. by naming with two leading underscores those attributes and methods that are only needed for a given class's internal operation.

PolySingleton and PolyBorg

One claimed advantage of Singleton, that Borg may not appear to match, is Consequence n.4, "Permits a variable number of instances". When you're refactoring your code, you can of course easily de-Borgize any given application level class, but there's no easy third way: it's all or nothing — either per instance state is shared, or it's kept by each instance on its own. On the other hand, a separate PolyBorg class isn't any harder to envisage, than the PolySingleton class the Gof4 may have had in mind when writing about this Consequence. Suppose, for example, that, in a given use-case of PolySingleton, exactly 4 instances may exist, and each call to PolySingleton::Instance chooses which of the 4 instances to yield in round robin fashion, something like:

PolySingleton* PolySingleton::_instances[4];
int PolySingleton::_next=-1;

PolySingleton* PolySingleton::Instance() {
    ++_next;
    if(_next>=4) _next=0;
    if(_instances[_next]==0) {
        _instances[_next] = new PolySingleton;
    }
    return _instances[_next];
}
I'm not too sure this variation makes much sense, but perhaps there are cases in which it does, e.g. for load balancing. More often, I suspect a class with a limited number of instances would require some kind of selector argument for instance selection. It typically does matter which one of the separate instances you get. However, such an extra argument would make PolySingleton not interface compatible with Singleton any more. But anyway, if this version of PolySingleton meets requirements, so does the following version of PolyBorg:
class PolyBorg:
    _shared_states = [{} for i in range(4)]
    _next = -1
    def __init__(self):
        self.__class__._next += 1
	if self.__class__._next>=4: self.__class__._next = 0
        self.__dict__ = self._shared_states[self.__class__._next]
We do of course have to be explicit and use self.__class__._next, rather than self._next. This is mandatory when we rebind it: otherwise, it would uselessly become per instance, while it's crucial that it stay per-class. For uniformity, we then obviously choose to use the explicit form throughout.

Borg, and its variation PolyBorg, are the first two of our five easy non-Patterns. They are as easy as pie, mind you. Don't let the amount of discussion fool you into believing there's anything deep or difficult about them. The discussion is mostly addressing the complexity and hidden depths of Singleton (and PolySingleton). Look at the Python code again: it's so much terser, clearer, and simpler than the discussion! Four lines of code for Borg, seven for PolyBorg, all clear and open and understandable at an elementary level. Python makes it easy to unveil the simplicity that, without it, masquerades as complexity.

But aren't these Patterns...?

So, what makes Borg and PolyBorg non-patterns? Why, if nothing else, they miss the prime requisite: Known Uses! That's right, folks, these are scary, dangerous, field-unproven, experimental ideas! Does this scare you off from the effort of studying and understanding them so you can apply them to everyday problems...? What effort? what study? they are clear at first sight to any Pythonista worth his or her salt. Indeed, they're so simple they have no doubt been independently reinvented over and over, as is often the case in Python. It's so simple to just do it, that the effort of combing the literature and published sources looking for Patterns to extract is sometimes hard to cost-justify. Sure, Python's readability and clarity reduce the cost of such a literature-search effort, but they cannot reduce it to the point of making it lower than the effort of writing four short lines of code, or thereabouts. So, Borg and PolyBorg are not Patterns (with the uppercase-P:-) because they're too simple and elementary to justify a Pattern's necessary "infrastructure" investment, particularly the research effort needed to find Known Uses. Do not forget that real Design Patterns do really need that infrastructure, and most particularly that research into Known Uses. Let's say Borg and PolyBorg are idioms, then, or, at best, lowercase-p patterns. I first wrote up Borg in [ma01a] as a Recipe, and that may be as good a name as any for this category of simple Pythonic ideas.

RegisBorg and RegisFact

Going back to the requisite of "multiple instances, but in limited number", we might typically want each instance-request to have as an argument a desired-instance identifier, say a string. If an instance corresponding to the given identifier already exists, that instance must be returned; otherwise, a new instance must be created and returned. This is getting pretty far away from the "just one instance" idea, and yet it is a reasonably frequent application need. Think, for example, of opening files, or other URLs: we may well want to ensure that state is shared, no matter how many times an URL is opened. Also, requirements akin to these are what the "registry of singletons" in the Gof4's Singleton DP Implementation section strongly suggests (at least, to me). Could Borg be stretched to accommodate this need — an extensible registry of hive-minds, tagged by identifiers, with the right one available on demand...?

Well, yes, it's not all that different from PolyBorg after all, and of course Python's dictionaries make "the Registry" a snap:

class RegisBorg:
    _shared_states = {}
    def __init__(self, ident):
        try: self.__dict__ = self._shared_states[ident]
	except KeyError: self.__dict__ = self._shared_states[ident] = {}

It is a snap, but one must nevertheless question if it's the appropriate snap, or if we couldn't have snapped even more simply and fruitfully. After all, the amended specs sound more and more like a Creational request, and yet we're still using a solution that's rather Structural. Such a "category mismatch" should rightfully makes us a little bit uneasy. Aren't we over-stretching Borg? Are we abandoning the straight and narrow, but fruitful, path of simplicity, for a seductive but ultimately fraught one of cleverness and deviousness...? After all, the "intuitive" solution to the stated requirements, the one that comes to mind at once, would be an application of a Pattern (or pattern, or idiom, or recipe...) "Factory with a Registry", or RegisFact for short:

class Whatever: pass
_instances = {}
def RegisFact(ident):
    try: return _instances[ident]
    except KeyError:
        _instances[ident] = Whatever()
	return _instances[ident]
RegisFact uses a Creational idea to implement something that feels very much like a Creational requirement. No "category mismatch", then, and surely, no cleverness, no deviousness. Let's note, in passing, that the Creational idea is a Factory, but a trivially simple one — just a function, how un-OO! — not one of the powerful Creational patterns, such as Abstract Factory and Factory Method. This is another non-Pattern, deliberately choosing simplicity over power.

However, RegisFact does not really achieve all that extra simplicity over RegisBorg, and it does have substantially limited functionality. Abstract, semi-philosophical guidelines such as "no category mismatch" are often useful as rules of thumb. Lighthouses help us find our way in the fog. But it's even better when there is no fog around, and we can just find our way by carefully examining our surroundings. Python's already-mentioned Sun is one good way to help fog disperse. Translation from fancy metaphors back down to Earth again: writing down some actual Python code, rather than reasoning in the abstract, we can more easily examine concrete perspectives of different possible solutions.

RegisBorg still has the same key useful aspect as the other Borg variations. We can subclass at will, to tweak behavior or add per-subclass state, while keeping the essential defining characteristic: all instances (here, all instances corresponding to a given ident) share state. Because of this, RegisBorg, exactly as above coded, is already useful, although class RegisBorg itself does just about nothing. You could say the non-pattern is factored as follows: class RegisBorg handles the registry and state-sharing behavior, while subclasses provide application-specific parts of behavior and state.

On the other hand, RegisFact, as coded, is not all that useful. Factory functions do not let client-code easily use inheritance. class Whatever is thus hard-coded with a certain behavior: as we wrote it, no behavior at all. Therefore, that's the behavior (or lack thereof) that actually obtains from the point of view of RegisFact's clients. Better than nothing: we can still use the Whatever instances, that factory function RegisFact yields, as passive containers of arbitrary attributes. Maybe we've even found the one case where adding per-instance behavior is justifiable!-). Still, RegisFact is far from being fully satisfactory, because of these limitations.

For most potential uses, RegisFact as written is too simple. Yes, there can be such a thing as an artifact that is too simple, not rich and complex enough, for the goals it aims to accomplish. RegisFact really wants to be quite a bit richer and more complex than it is, to deploy its full potential. Factories' specialty is their potential ability to return objects of different classes, according to specifics that the Factory can encapsulate. RegisFact could be extended to hold a registry of classes. It could further hold a set of rules (the Strategy Design Pattern might be very appropriate here!) to select the right class to instantiate for any given requested ident — parsing the ident string, for example, and selecting accordingly. But it doesn't do any of that, as written... it just sort of sits there! So, RegisFact as written is "easy", yes, but... too easy — neither fish nor fowl, neither as intrinsically simple as RegisBorg, nor as rich and sophisticated as the full-fledged Design Pattern that RegisFact might one day become, RegisFact as it stands basically serves the purpose of convincing us that a thorough study of Design Patterns and Pattern Languages is anything but a waste of effort. Simplicity sometimes can, for a short while (until we examine it more closely, in bright light, and check if it sparkles, or starts getting soggy and melting) be somewhat illusory in its intuitive appeal!

Borg 2.2?

For completeness, and to reach the number of five non-patterns and justify the neat title, let's see how Borg interacts with the "non-classic" (a.k.a. "new-style") classes introduced in Python 2.2. The "instant user appeal" of the new-style classes is, first and foremost, that such classes let you subclass built-in types. However, new-style classes come with a whole panoplia of new possibilities and constraints. From the perspective of sharing state, in particular, the key difference from a classic class is that a new-style class doesn't keep all per instance data in a single dictionary. The class may inherit from a built-in type, which may keep some per-instance state wherever it pleases; also, a class may define or inherit a __slots__ attribute, in which case per-instance state lives in the slots rather than in a dictionary. Moreover, even for new-style classes whose instances do keep state in __dict__, the __dict__ attribute itself may not be assigned (re-bound).

A new-style class, therefore, cannot just inherit a mix-in like Borg and have all per instance state become automatically shared, as a classic class can. Rather, with new-style classes, we are back to the "status quo" as in most other languages: to share state, we must rely on Delegation.

Note that Delegation is not a Design Pattern, as the Gof4 explain well in their book: it is just too fundamental, too basilar to good object-oriented design. Delegation is not a DP for much the same reason such things as integer addition, while loops, or subroutines aren't DPs: they are, rather, some of the fundamental building blocks out of which all designs, and their inherent patterns (or Patterns), are built. The Gof4 list Delegation among the fundamental principles, right after Polymorphism, Mixin classes, "Program to an interface, not an implementation" (Python translation: don't type-test!-), and "Favor object composition over class inheritance"; and just before Generics and the principles of "Designing for change". All in Chapter 1, of course, before they start their Design Patterns catalog.

In Python, we're blessed with a particularly flexible and easy to code form of automatic Delegation. Special methods __getattr__ and friends are strategic choke-points, from which we can easily control and divert (e.g., delegate) any attribute and method access (and binding, re-binding, unbinding). In pre-2.2 Python, we used such automatic Delegation, for example, to "inherit" (so to speak) from built-in types, as shown in [ma01b]. We couldn't actually inherit, but we almost didn't notice, except where some uncouth piece of framework or client code type-tested, and thus broke the wonderful, smooth polymorphism. To quote the Gof4 again, "Delegation ... shows ... you can always replace inheritance with object composition" — unless, of course, somebody's busy coding deuced type-tests. In Python 2.2, we don't need automatic Delegation to pseudo-subclass built-in types, as we can subclass them in earnest. However, automatic delegation is anything but obsolete. Old non-Patterns don't really ever die, they just fade away into somewhat more obscure corners of language use.

Automatic Delegation still plays a precious role in Python 2.2. Consider, for example:

class DeleBorg:
    _delegate = None
    def __getattr__(self,name):
        return getattr(self._delegate,name)
    def __delattr__(self,name):
        return delattr(self._delegate,name)
    def __setattr__(self,name,value):
        return setattr(self._delegate,name,value)
As coded, class DeleBorg is a classic class, but it might equally well be made into a new-style class, by having it inherit from object, since the triad of methods __getattr__, __delattr__, __setattr__ would still work. All substantial behavior, as well as all state, comes from the self._delegate object, since methods are accessed through __getattr__, just like any other attribute.

While the "data override" (of attribute _shared_state) was optional for subclasses of Borg, we do need an analogous "data override" (of the _delegate attribute) to make subclasses of DeleBorg useful:

class Borg22(DeleBorg):
    _delegate = object_to_be_wrapped
Again, therefore, DeleBorg's methods access self._delegate, not DeleBorg._delegate, so as to enable the "data override" by subclasses. For the same reason, we name the overridable attribute with one leading underscore, not two. This is even more important for DeleBorg than it was for Borg, since the "data override" plays such a central role here, while previously it was just a nice option we wanted to preserve.

However, not all is perfectly rosy here, alas.

DeleBorg is not quite as neat as Borg itself: it's not such a direct expression of design intent. Rather than being able to share state directly, we share it indirectly, by taking control of the behavioral aspects of accessing, binding, re-binding, and unbinding elements of the state. Thus, we have over twice the boilerplate code (albeit still in a modest amount), and a small but non-null overhead, an extra call for any operation. Further, instances of subclasses of DeleBorg do not satisfy isinstance with the type or class of the _delegate attribute, while instances of subclasses of Borg, thanks to multiple inheritance, did. Besides the bother of type-tests, this means, for example, that client code becomes constrained with respect to extracting and applying unbound methods from this class, or type.

These are disturbances at the margin, rather than crucial defects, but still they show that DeleBorg isn't quite as seamless, nor quite as big a win, as Borg used to be, pre-2.2. Borg still lives, therefore, even in Python 2.2: if the class we want to Borgize is a classic one, using Borg itself still has advantages over using DeleBorg.

Conclusion

Design Patterns, and Pattern Languages, are very useful conceptual tools: they can help you think effectively about design, as well as providing immediately useful ways to frame specific design problems and their solutions. However, not all design ideas are Design Patterns, nor should they all be.

Some design ideas and approaches are too fundamental, basilar, pervasive, to be classified as Design Patterns. Others are too simple, elementary, intuitive, to be worth classifying as DPs. Such a classification is at least a semi-formal endeavor, requiring a definite amount of work (particularly to research and document Known Uses, an indispensable step). The work should be undertaken only when there is enough "substance" in the prospective DP to pay back the effort expended, with interest. Moreover, which design ideas it's feasible to classify as DPs, and which ideas are worth thus classifying, does depend on the programming language meant to be used to implement the design. Design is not an abstract, in-a-vacuum activity: rather, it is a concrete bridge between analysis and intended implementation, with a lot of "feedback" between the various phases.

These theses aren't all that controversial: indeed, they are asserted and argued in the very first chapter of the "Design Patterns" book! However, it appears that many readers of that excellent work skim its beginning lightly, eager to jump into the "meat" of the DP catalog that makes up most of the book. This is surely understandable, as the catalog is so rapidly useful to help with real-life problems. The start of the book, in contrast, may look like abstract, generic introductory and philosophical material, not immediately usable. However, such readers are shortchanging themselves, by not acquiring the meta-tools needed for critical analysis of specific design needs in term of DPs, and vice versa. Critical analysis of DPs and design needs is not an optional issue: without it, you cannot spot what patterns are anti-patterns, in terms of your actual design needs — including what programming languages you intend to target with your design.

In this paper, I single out one Design Pattern, the popular Singleton, for critical examination. I survey its applicability, both in general terms, and, more specifically, with regards to two subtly different languages, Python 2.1 and 2.2. I propose and examine alternative design ideas (not full fledged Patterns) addressing Forces very similar to the ones Singleton deals with, in very different ways (Structural, or even Behavioral, as opposed to Creational ones). The alternative ideas are quite simple, which is most often a very good thing. However, I also show one case in which excessive (misplaced) simplicity makes a design idea not very useful for our purposes: we do want to make our designs as simple as possible... but, no simpler than that!

References

[al77]C. Alexander, et al, "A Pattern Language: Towns, Buildings, Construction", Oxford University Press 1977

[al79]C. Alexander, "The Timeless Way of Building", Oxford University Press 1979

[bd00]"Big Design Up Front", multi-author WikiWiki, http://xp.c2.com/BigDesignUpFront.html

[be00]J. Bentley, "Programming Pearls", Second Edition, Addison-Wesley 2000

[fo97]M. Fowler, "Analysis Patterns: Reusable Object Models", Addison-Wesley 1997

[fo99]M. Fowler, "Refactoring: Improving the Design of Existing Code", Addison-Wesley Longman 1999

[go95]E. Gamma, R. Helm, R. Johnson, J. Vlissides, "Design Patterns, Elements of Reusable Object-Oriented Software", Addison-Wesley 1995

[ko33]A. Korzybski, "Science and Sanity: An Introduction to Non-Aristotelian Systems and General Semantics", first published 1933; reprint [Science Press] 1961

[ma01a]A. Martelli, "Singleton? We don't need no stinkin' singleton: the Borg design non-pattern", in "Python Cookbook", http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/66531

[ma01b]A. Martelli, "Automatic delegation as an alternative to inheritance", in "Python Cookbook", http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52295

[sa23]G. Santayana, "Skepticism and Animal Faith: Introduction to a System of Philosophy", first published 1923; reprint [Dover] 1955

[sa98]V. Savikko, "Design Patterns in Python", in "Proceedings of the 6th International Python Conference", http://www.python.org/workshops/1997-10/proceedings/savikko.html

[sa99]N. Salingaros, "The Structure of Pattern Languages", in "arq -- Architectural Research Quarterly" volume 4 (2000), http://www.math.utsa.edu/sphere/salingar/StructurePattern.html

[vl98]J. Vlissides, "Pattern Hatching: Design Patterns Applied", Addison-Wesley 1998

[wh96]R. G. White, "Advantages and disadvantages of unique representation patterns", C++ Report 8-8 pp 28-25, Sep 1996

[wi22]L. von Wittgenstein, "Logische-Philosophische Abhandlung", Annalen der Naturphilosophie 1922; bilingual edition (German/English) as "Tractatus Logico-Philosophicus" [Routledge] 1924