预计阅读本页时间:-
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).