问题
Possible Duplicate:
Is there a simple, elegant way to define Singletons in Python?
I have the following example code, in which I derive a class from a Singleton (hope it is one):
class Singleton(object):
_instance = None
def __new__(cls, *args, **kwargs):
if not cls._instance:
cls._instance = object.__new__(cls, *args, **kwargs)
return cls._instance
class Tracer(Singleton):
def __init__(self):
print "Init"
a = Tracer()
b = Tracer()
When you try it, you will see the __init__
method of Tracer
is called again. Isn't the sense of having a singleton to make another instance refer to the original one? I do not want to run the __init__
method again, as it probably overwrites previous information. Maybe the singleton is wrong or it's use?
回答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:
class Singleton(type):
"""Metaclass."""
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
return cls._instances[cls]
class Tracer(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 python3
class Tracer(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?
class Tracer(object):
__metaclass__ = Singleton
def __init__(self):
print("Init")
else: # Python 3
class Tracer(object, metaclass=Singleton): # causes SyntaxError in Python 2
def __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:
class Tracer(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.)
回答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.
来源:https://stackoverflow.com/questions/13789235/how-to-initialize-singleton-derived-object-once