这个问题不是为了讨论单例设计模式是否可取、是否是一种反模式,或者是否用于任何宗教战争,而是为了讨论如何以最Python化的方式在Python中最好地实现这种模式。在这个例子中,我定义“最蟒蛇”是指它遵循“最少惊讶的原则”。

我有多个类将成为单类(我的用例是一个记录器,但这并不重要)。当我可以简单地继承或装饰时,我不希望在几个类中添加口香糖。

最佳方法:


方法1:装饰器

def singleton(class_):
    instances = {}
    def getinstance(*args, **kwargs):
        if class_ not in instances:
            instances[class_] = class_(*args, **kwargs)
        return instances[class_]
    return getinstance

@singleton
class MyClass(BaseClass):
    pass

Pros

装饰符的添加方式通常比多重继承更直观。

Cons

虽然使用MyClass()创建的对象将是真正的单例对象,但MyClass本身是一个函数,而不是类,因此不能从中调用类方法x=MyClass();y=MyClass();t=类型(n)();

则x==y但x!=t&y!=吨


方法2:基类

class Singleton(object):
    _instance = None
    def __new__(class_, *args, **kwargs):
        if not isinstance(class_._instance, class_):
            class_._instance = object.__new__(class_, *args, **kwargs)
        return class_._instance

class MyClass(Singleton, BaseClass):
    pass

Pros

这是一门真正的课

Cons

多重继承-嗯__new__是否可以在从第二个基类继承期间被覆盖?一个人必须想得比必要的多。


方法3:元类

class Singleton(type):
    _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]

#Python2
class MyClass(BaseClass):
    __metaclass__ = Singleton

#Python3
class MyClass(BaseClass, metaclass=Singleton):
    pass

Pros

这是一门真正的课自动神奇地覆盖继承为正确的目的使用__metaclass__(并让我意识到)

Cons

有吗?


方法4:decorator返回同名类

def singleton(class_):
    class class_w(class_):
        _instance = None
        def __new__(class_, *args, **kwargs):
            if class_w._instance is None:
                class_w._instance = super(class_w,
                                    class_).__new__(class_,
                                                    *args,
                                                    **kwargs)
                class_w._instance._sealed = False
            return class_w._instance
        def __init__(self, *args, **kwargs):
            if self._sealed:
                return
            super(class_w, self).__init__(*args, **kwargs)
            self._sealed = True
    class_w.__name__ = class_.__name__
    return class_w

@singleton
class MyClass(BaseClass):
    pass

Pros

这是一门真正的课自动神奇地覆盖继承

Cons

创建每个新类是否没有开销?在这里,我们为每个希望成为单例的类创建两个类。虽然这对我来说很好,但我担心这可能无法扩展。当然,对于这种模式的规模是否太容易,还有争议。。。_seled属性的意义是什么无法使用super()在基类上调用同名方法,因为它们将递归。这意味着您不能自定义__new__,也不能对需要调用__init__的类进行子类化。


方法5:模块

模块文件singleton.py

Pros

简单胜于复杂

Cons

未延迟实例化


当前回答

这里是一个结合@agf和@(Siddhesh Suhas Sathe)解决方案的简单实现,它使用元类并考虑构造函数参数,因此如果使用完全相同的参数创建foo类,则可以返回相同的实例


class SingletonMeta(type):
    _instances = {}

    def __call__(cls, *args, **kwargs):
        """
        Possible changes to the value of the `__init__` argument do not affect
        the returned instance.
        """
        cls_instances = cls._instances.get(cls) or []
        matching_instances = list(
            filter(
                lambda x: x["args"] == args and x["kwargs"] == kwargs,
                cls_instances,
            )
        )
        if len(matching_instances) == 1:
            return matching_instances[0]["instance"]
        else:
            instance = super().__call__(*args, **kwargs)
            cls_instances.append({"instance": instance, "args": args, "kwargs": kwargs})
            cls._instances[cls] = cls_instances
            return instance


class foo(metaclass=SingletonMeta):
    def __init__(self, param, k_param=None) -> None:
        print("Creating new instance")
        self.param = param
        self.k_param = k_param
        self._creation_time = time.time()

其他回答

好吧,除了同意Pythonic关于模块级全局的一般建议之外,这又如何呢

def singleton(class_):
    class class_w(class_):
        _instance = None
        def __new__(class2, *args, **kwargs):
            if class_w._instance is None:
                class_w._instance = super(class_w, class2).__new__(class2, *args, **kwargs)
                class_w._instance._sealed = False
            return class_w._instance
        def __init__(self, *args, **kwargs):
            if self._sealed:
                return
            super(class_w, self).__init__(*args, **kwargs)
            self._sealed = True
    class_w.__name__ = class_.__name__
    return class_w

@singleton
class MyClass(object):
    def __init__(self, text):
        print text
    @classmethod
    def name(class_):
        print class_.__name__

x = MyClass(111)
x.name()
y = MyClass(222)
print id(x) == id(y)

输出为:

111     # the __init__ is called only on the 1st time
MyClass # the __name__ is preserved
True    # this is actually the same instance

这与fab的答案略有相似,但并不完全相同。

单例模式不要求我们能够多次调用构造函数。作为一个单例应该创建一次,而且只能创建一次。难道它不应该被视为只创建一次吗?“欺骗”构造函数可能会损害易读性。

所以我的建议是:

class Elvis():
    def __init__(self):
        if hasattr(self.__class__, 'instance'):
            raise Exception()
        self.__class__.instance = self
        # initialisation code...

    @staticmethod
    def the():
        if hasattr(Elvis, 'instance'):
            return Elvis.instance
        return Elvis()

这并不排除用户代码使用构造函数或字段实例:

if Elvis() is King.instance:

…如果你确定猫王还没有被创造,而国王已经被创造了。

但它鼓励用户普遍使用以下方法:

Elvis.the().leave(Building.the())

若要完成此操作,您还可以重写__delattr__()以在尝试删除实例时引发异常,并重写__del__()以便引发异常(除非我们知道程序正在结束…)

进一步改进


我感谢那些帮助评论和编辑的人,欢迎更多评论和编辑。当我使用Jython时,这应该更普遍地工作,并且是线程安全的。

try:
    # This is jython-specific
    from synchronize import make_synchronized
except ImportError:
    # This should work across different python implementations
    def make_synchronized(func):
        import threading
        func.__lock__ = threading.Lock()
    
        def synced_func(*args, **kws):
            with func.__lock__:
                return func(*args, **kws)

        return synced_func

class Elvis(object): # NB must be subclass of object to use __new__
    instance = None

    @classmethod
    @make_synchronized
    def __new__(cls, *args, **kwargs):
        if cls.instance is not None:
            raise Exception()
        cls.instance = object.__new__(cls, *args, **kwargs)
        return cls.instance
    
    def __init__(self):
        pass
        # initialisation code...

    @classmethod
    @make_synchronized
    def the(cls):
        if cls.instance is not None:
            return cls.instance
        return cls()

注意事项:

如果您不从python2.x中的对象子类,您将得到一个不使用__new的旧式类__当修饰__new__时,必须用@classmethod修饰,否则__new__将是未绑定的实例方法这可以通过使用元类来改进,因为这将允许您创建类级属性,并可能将其重命名为实例

方法:一次性使用后重写__new__

class Singleton():
    def __init__(self):
        Singleton.instance = self
        Singleton.__new__ = lambda _: Singleton.instance

Pros

极其简洁真正的类,不需要模块正确使用lambda和蟒猴补丁

Cons

__new__可能再次被覆盖

您可能永远不需要Python中的单例。只需在一个模块中定义所有数据和函数,就可以得到事实上的单例:

import datetime
file_name=None

def set_file_name(new_file_name: str):
    global file_name
    file_name=new_file_name

def write(message: str):
    global file_name
    if file_name:
        with open(file_name, 'a+') as f:
            f.write("{} {}\n".format(datetime.datetime.now(), message))
    else:
        print("LOG: {}", message)

要使用:

import log
log.set_file_name("debug.log")
log.write("System starting")
...

如果你真的必须有一个单独的类,那么我会选择:

class MySingleton(object):
    def foo(self):
        pass

my_singleton = MySingleton()

要使用:

from mysingleton import my_singleton
my_singleton.foo()

其中mysingleton.py是定义mysingleton的文件名。这是因为在第一次导入文件后,Python不会重新执行代码。

如果您不需要对Singleton实例进行延迟初始化,那么以下操作应该是简单且线程安全的:

class A:
    instance = None
    # Methods and variables of the class/object A follow
A.instance = A()

这样,A是在模块导入时初始化的单例。