Singleton Classes

Because class decorators may intercept instance creation calls, they can be used to either manage all the instances of a class, or augment the interfaces of those instances. To demonstrate, here’s a first class decorator example that does the former—managing all instances of a class. This code implements the classic singleton coding pattern, where at most one instance of a class ever exists. Its singleton function defines and returns a function for managing instances, and the @ syntax automatically wraps up a subject class in this function:

instances = {}
def getInstance(aClass, *args):                 # Manage global table
    if aClass not in instances:                 # Add **kargs for keywords
        instances[aClass] = aClass(*args)       # One dict entry per class
    return instances[aClass]

def singleton(aClass):                          # On @ decoration
    def onCall(*args):                          # On instance creation
        return getInstance(aClass, *args)
    return onCall

广告:个人专属 VPN,独立 IP,无限流量,多机房切换,还可以屏蔽广告和恶意软件,每月最低仅 5 美元

To use this, decorate the classes for which you want to enforce a single-instance model:

@singleton                                      # Person = singleton(Person)
class Person:                                   # Rebinds Person to onCall
     def __init__(self, name, hours, rate):     # onCall remembers Person
        self.name = name
        self.hours = hours
        self.rate = rate
     def pay(self):
        return self.hours * self.rate

@singleton                                      # Spam = singleton(Spam)
class Spam:                                     # Rebinds Spam to onCall
    def __init__(self, val):                    # onCall remembers Spam
        self.attr = val

bob = Person('Bob', 40, 10)                     # Really calls onCall
print(bob.name, bob.pay())

sue = Person('Sue', 50, 20)                     # Same, single object
print(sue.name, sue.pay())

X = Spam(42)                                    # One Person, one Spam
Y = Spam(99)
print(X.attr, Y.attr)

Now, when the Person or Spam class is later used to create an instance, the wrapping logic layer provided by the decorator routes instance construction calls to onCall, which in turn calls getInstance to manage and share a single instance per class, regardless of how many construction calls are made. Here’s this code’s output:

Bob 400
Bob 400
42 42

Interestingly, you can code a more self-contained solution here if you’re able to use the nonlocal statement (available in Python 3.0 and later) to change enclosing scope names, as described earlier—the following alternative achieves an identical effect, by using one enclosing scope per class, instead of one global table entry per class:

def singleton(aClass):                          # On @ decoration
    instance = None
    def onCall(*args):                          # On instance creation
        nonlocal instance                       # 3.0 and later nonlocal
        if instance == None:
            instance = aClass(*args)            # One scope per class
        return instance
    return onCall

This version works the same, but it does not depend on names in the global scope outside the decorator. In either Python 2.6 or 3.0, you can also code a self-contained solution with a class instead—the following uses one instance per class, rather than an enclosing scope or global table, and works the same as the other two versions (in fact, it relies on the same coding pattern that we will later see is a common decorator class blunder; here we want just one instance, but that’s not always the case):

class singleton:
    def __init__(self, aClass):                 # On @ decoration
        self.aClass = aClass
        self.instance = None
    def __call__(self, *args):                  # On instance creation
        if self.instance == None:
            self.instance = self.aClass(*args)  # One instance per class
        return self.instance

To make this decorator a fully general-purpose tool, store it in an importable module file, indent the self-test code under a __name__ check, and add support for keyword arguments in construction calls with **kargs syntax (I’ll leave this as a suggested exercise).