How To Initialize Singleton-derived Object Once
Solution 1:
My previous answer didn't work and I've deleted it. However I've found a highly rated SO answer that does. The primary differences are that it uses a Singleton
metaclass instead of a baseclass and overloads the __call__()
method of its instance classes instead of their __new__()
method. This gives it the control required over the creation process of instances of its singleton class instances. It would be possible to define an additional method for deleting one or more of these — say for testing purposes.
Another notable implementation detail is that the metaclass maintains a dictionary of _instances
rather than something that can only hold a single value. This allows it keep track of an indefinite number of singleton instances (since it might be the metaclass of more than one since it's reusable).
Applying it to your sample code would be done something like this:
classSingleton(type):
"""Metaclass."""
_instances = {}
def__call__(cls, *args, **kwargs):
if cls notin cls._instances:
cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
return cls._instances[cls]
classTracer(object):
__metaclass__ = Singleton
def__init__(self):
print("Init")
a = Tracer()
b = Tracer()
print('a is b: {}'.format(a is b)) # same object? -> True
Output:
Init
a is b: True
Update
The syntax for specifying a metaclass varies between Python 2 and 3. For the latter you'd need to change the Tracer
class definition to this:
#!/usr/bin/env python3classTracer(object, metaclass=Singleton):
def__init__(self):
print("Init")
Writing a something that would work in both version 2 and 3 of Python is possible, but is a little more complicated since you can't simply conditionally define it like this:
## Won't work ##if sys.version_info[0] < 3: # Python 2?classTracer(object):
__metaclass__ = Singleton
def__init__(self):
print("Init")
else: # Python 3classTracer(object, metaclass=Singleton): # causes SyntaxError in Python 2def__init__(self):
print("Init")
because the definition in the else
clause causes a SyntaxError
in Python 2 (even though the code in the block will never actually be executed). A workaround similar to what Benjamin Peterson's six module's with_metaclass()
function does and would look like this:
classTracer(Singleton("SingletonBaseClass", (object,), {})):
def__init__(self):
print("Init")
This dynamically creates a baseclass that inherits the desired metaclass—thereby avoiding any errors due to metaclass syntax differences between the two Python versions. (Which it does by explicitly using the defined metaclass to create the temporary baseclass.)
Solution 2:
Your __init__
is called twice, but on the same object. You have created a singleton, but Python doesn't know it is, so it initializes each object that gets created.
If you want to pursue the singleton pattern, you'll have to move your initializing code into the __new__
, or into another method that your __new__
calls.
Keep in mind:
Singletons are the norm in Java, but are frowned upon in Python.
Singletons make your code harder to test, because they are global state carried from one test to the next.
Post a Comment for "How To Initialize Singleton-derived Object Once"