Is there a simple, elegant way to define singleton

2018-12-31 04:36发布

This question already has an answer here:

There seem to be many ways to define singletons in Python. Is there a consensus opinion on Stack Overflow?

22条回答
泪湿衣
2楼-- · 2018-12-31 05:20

Singleton's half brother

I completely agree with staale and I leave here a sample of creating a singleton half brother:

class void:pass
a = void();
a.__class__ = Singleton

a will report now as being of the same class as singleton even if it does not look like it. So singletons using complicated classes end up depending on we don't mess much with them.

Being so, we can have the same effect and use simpler things like a variable or a module. Still, if we want use classes for clarity and because in Python a class is an object, so we already have the object (not and instance, but it will do just like).

class Singleton:
    def __new__(cls): raise AssertionError # Singletons can't have instances

There we have a nice assertion error if we try to create an instance, and we can store on derivations static members and make changes to them at runtime (I love Python). This object is as good as other about half brothers (you still can create them if you wish), however it will tend to run faster due to simplicity.

查看更多
刘海飞了
3楼-- · 2018-12-31 05:21

Here's my own implementation of singletons. All you have to do is decorate the class; to get the singleton, you then have to use the Instance method. Here's an example:

@Singleton
class Foo:
   def __init__(self):
       print 'Foo created'

f = Foo() # Error, this isn't how you get the instance of a singleton

f = Foo.instance() # Good. Being explicit is in line with the Python Zen
g = Foo.instance() # Returns already created instance

print f is g # True

And here's the code:

class Singleton:
    """
    A non-thread-safe helper class to ease implementing singletons.
    This should be used as a decorator -- not a metaclass -- to the
    class that should be a singleton.

    The decorated class can define one `__init__` function that
    takes only the `self` argument. Also, the decorated class cannot be
    inherited from. Other than that, there are no restrictions that apply
    to the decorated class.

    To get the singleton instance, use the `instance` method. Trying
    to use `__call__` will result in a `TypeError` being raised.

    """

    def __init__(self, decorated):
        self._decorated = decorated

    def instance(self):
        """
        Returns the singleton instance. Upon its first call, it creates a
        new instance of the decorated class and calls its `__init__` method.
        On all subsequent calls, the already created instance is returned.

        """
        try:
            return self._instance
        except AttributeError:
            self._instance = self._decorated()
            return self._instance

    def __call__(self):
        raise TypeError('Singletons must be accessed through `instance()`.')

    def __instancecheck__(self, inst):
        return isinstance(inst, self._decorated)
查看更多
不流泪的眼
4楼-- · 2018-12-31 05:23

There are also some interesting articles on the Google Testing blog, discussing why singleton are/may be bad and are an anti-pattern:

查看更多
泪湿衣
5楼-- · 2018-12-31 05:24

I think that forcing a class or an instance to be a singleton is overkill. Personally, I like to define a normal instantiable class, a semi-private reference, and a simple factory function.

class NothingSpecial:
    pass

_the_one_and_only = None

def TheOneAndOnly():
    global _the_one_and_only
    if not _the_one_and_only:
        _the_one_and_only = NothingSpecial()
    return _the_one_and_only

Or if there is no issue with instantiating when the module is first imported:

class NothingSpecial:
    pass

THE_ONE_AND_ONLY = NothingSpecial()

That way you can write tests against fresh instances without side effects, and there is no need for sprinkling the module with global statements, and if needed you can derive variants in the future.

查看更多
牵手、夕阳
6楼-- · 2018-12-31 05:26

My simple solution which is based on the default value of function parameters.

def getSystemContext(contextObjList=[]):
    if len( contextObjList ) == 0:
        contextObjList.append( Context() )
        pass
    return contextObjList[0]

class Context(object):
    # Anything you want here
查看更多
永恒的永恒
7楼-- · 2018-12-31 05:27
class Singleton(object[,...]):

    staticVar1 = None
    staticVar2 = None

    def __init__(self):
        if self.__class__.staticVar1==None :
            # create class instance variable for instantiation of class
            # assign class instance variable values to class static variables
        else:
            # assign class static variable values to class instance variables
查看更多
登录 后发表回答