Python中实现单例模式时,为什么仿照经典代码却出现了非单例状态,代码哪里出错了?


Python中实现单例模式时,为什么仿照经典代码却出现了非单例状态,代码哪里出错了?
15 回复

python 中直接模块级常量就是单例


你遇到的问题很常见,通常是因为对Python的模块导入机制和类变量作用域理解不够。经典的单例模式实现(比如用__new__方法)在大多数情况下是有效的,但如果在多线程环境或者模块导入方式特殊时,可能会被意外打破。

最常见的一个错误是:在类定义内部直接实例化,并且这个实例化代码在模块导入时被执行了多次。比如下面这个有问题的写法:

class Singleton:
    _instance = None

    def __new__(cls):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
        return cls._instance

# 错误:在类定义后立即创建实例
singleton = Singleton()

如果这个模块被多个地方以不同方式导入(比如相对导入和绝对导入混用),singleton = Singleton()这行代码可能会执行多次,导致实际上创建了多个“单例”实例。

正确的做法是延迟实例化,并且确保实例化只发生一次。更健壮的实现:

class Singleton:
    _instance = None
    _initialized = False

    def __new__(cls):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
        return cls._instance

    def __init__(self):
        if not self._initialized:
            # 你的初始化代码
            self._initialized = True

# 使用时再实例化,不要提前创建
def get_singleton():
    return Singleton()

如果你用的是装饰器方式,也要注意装饰器是否被多次应用。另外,检查一下你的导入语句,确保没有循环导入或者重复导入的情况。

建议:用__new___initialized标志来确保真正的单例。

我的第一处代码是仿照经典实现, 而且自己也看不少书, 都是推荐这种写法

我的疑惑在于: 是不是我写错了 ?

单例模式 建议用 new,不是用 init

应该用__new__只创建一个实例

你这是那里看到的推荐写法,“看了不少书”?你可以列出书名。另外楼上都是正解

你这种写法只是共用一个类变量,但是每次都实例化 BackgroundScheduler

正解。

还有,看了你给的链接 https://github.com/faif/python-patterns/blob/master/creational/borg.py
人家写的没错,通过继承共用类变量来实现单例


是不是我还要继承下( 我原以为那个继承, 就是做做样子的, 直接使用基类不就完了 :P )
但是, 如果我使用 str 之类的, 就和他的完全保持一致了



使用 new , 见过, 但是没有深入了解其原理过


清一色的 Borg 推荐: https://www.google.com/search?q=python+singleton&oq=python+singleton+&aqs=chrome…69i57.6145j0j7&sourceid=chrome&ie=UTF-8


但是都加了一个继承, 看起来那个继承, 不是我认为「多余的」


:P



反正各种解决方法就是了, 大家也木有一个统一的意见, 貌似也木有最佳实践了

python 中最佳实践就是模块级常量

模块,简单安全

模块变量,简单的多线程锁就这么干的,跑的挺欢的

可以试下这个单例装饰器,定义类的时候加在前面就行

#!/usr/bin/env python
# -- coding: utf-8 --


class SingletonDecorator:
def init(self, klass):
self.klass = klass
self.instance = None

def call(self, *args, **kwds):
if self.instance is None:
self.instance = self.klass(*args, **kwds)
return self.instance

回到顶部