How to initialize Singleton-derived object once [duplicate]

旧城冷巷雨未停 提交于 2019-12-11 02:09:56

问题


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:

  1. Singletons are the norm in Java, but are frowned upon in Python.

  2. 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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!