Python: thinking of a module and its variables as a singleton — Clean approach?

后端 未结 6 2085
被撕碎了的回忆
被撕碎了的回忆 2020-12-13 13:21

I\'d like to implement some sort of singleton pattern in my Python program. I was thinking of doing it without using classes; that is, I\'d like to put all the singleton-rel

相关标签:
6条回答
  • 2020-12-13 13:34

    For a legitimate Singleton:

    class SingletonMeta(type):
        __classes = {} # protect against defining class with the same name
        def __new__(cls, cls_name, cls_ancestors, cls_dict): 
             if cls_name in cls.__classes:
                 return cls.__classes[cls_name]
             type_instance = super(SingletonMeta, cls).__new__(cls, cls_name, cls_ancestors, cls_dict) # pass 'type' instead of 'cls' if you dont want SingletonMeta's attributes reflected in the class
             return type_instance() # call __init__
    
    class Singleton:
        __metaclass__ = SingletonMeta
        # define __init__ however you want
    
        __call__(self, *args, *kwargs):
            print 'hi!'
    

    To see that it truly is a singleton, try to instantiate this class, or any class that inherits from it.

    singleton = Singleton() # prints "hi!"
    
    0 讨论(0)
  • 2020-12-13 13:39

    One approach to implementing a singleton pattern with Python can also be:

    have singleton __init()__ method raise an exception if an instance of the class already exists. More precisely, class has a member _single. If this member is different from None, exception is raised.

    class Singleton:
        __single = None
        def __init__( self ):
            if Singleton.__single:
                raise Singleton.__single
            Singleton.__single = self  
    

    It could be argued that handling the singleton instance creation with exceptions is not very clean also. We may hide implementation details with a method handle() as in

    def Handle( x = Singleton ):
        try:
            single = x()
        except Singleton, s:
            single = s
        return single 
    

    this Handle() method is very similar to what would be a C++ implementation of the Singleton pattern. We could have in Singleton class the handle()

    Singleton& Singleton::Handle() {
    
      if( !psingle ) {
        psingle = new Singleton;
      }
      return *psingle;
    }
    

    returning either a new Singleton instance or a reference to the existing unique instance of class Singleton.

    Handling the whole hierarchy

    If Single1 and Single2 classes derive from Singleton, a single instance of Singleton through one of the derived class exists. This can be verify with this:

    >>> child = S2( 'singlething' )
    >>> junior = Handle( S1)
    >>> junior.name()
    'singlething'
    
    0 讨论(0)
  • 2020-12-13 13:39

    Building off of WillYang's answer and taking it a step further for cleanliness: define a simple class to hold your global dictionary to make it easier to reference:

    class struct(dict):
        def __init__(self, **kwargs):
            dict.__init__(self, kwargs)
            self.__dict__ = self
    
    g = struct(var1=None, var2=None)
    
    def func():
        g.var1 = dict()
        g.var3 = 10
        g["var4"] = [1, 2]
        print(g["var3"])
        print(g.var4)
    

    Just like before you put anything you want in g but now it's super clean. :)

    0 讨论(0)
  • 2020-12-13 13:40

    A common alternative to using a module as a singleton is Alex Martelli's Borg pattern:

    class Borg:
        __shared_state = {}
        def __init__(self):
            self.__dict__ = self.__shared_state
        # and whatever else you want in your class -- that's all!
    

    There can be multiple instances of this class, but they all share the same state.

    0 讨论(0)
  • 2020-12-13 13:40

    Maybe you can put all the variables in a global dict, and you can directly use the dict in your functions without "global".

    # Singleton-related variables
    my_globals = {'foo': 'blah', 'bar':'stuff'}
    
    # Functions that process the above variables
    def work(some_parameter):
        if some_parameter:
            my_globals['bar'] = ...
        else:
            my_globals['foo'] = ...
    

    why you can do it like this is Python Scopes and Namespaces.

    0 讨论(0)
  • 2020-12-13 13:48

    Similar to Sven's "Borg pattern" suggestion, you could just keep all your state data in a class, without creating any instances of the class. This method utilizes new-style classes, I believe.

    This method could even be adapted into the Borg pattern, with the caveat that modifying the state members from the instances of the class would require accessing the __class__ attribute of the instance (instance.__class__.foo = 'z' rather than instance.foo = 'z', though you could also just do stateclass.foo = 'z').

    class State:    # in some versions of Python, may need to be "class State():" or "class State(object):"
        __slots__ = []   # prevents additional attributes from being added to instances and same-named attributes from shadowing the class's attributes
        foo = 'x'
        bar = 'y'
    
        @classmethod
        def work(cls, spam):
            print(cls.foo, spam, cls.bar)
    

    Note that modifications to the class's attributes will be reflected in instances of the class even after instantiation. This includes adding new attributes and removing existing ones, which could have some interesting, possibly useful effects (though I can also see how that might actually cause problems in some cases). Try it out yourself.

    0 讨论(0)
提交回复
热议问题