如何适应单例模式?(弃用警告)

15 投票
3 回答
4319 浏览
提问于 2025-04-16 19:06

几年前,我在网上找到了一种用Python实现单例模式的方法,作者是Duncan Booth

class Singleton(object):
    """
    Singleton class by Duncan Booth.
    Multiple object variables refers to the same object.
    http://web.archive.org/web/20090619190842/http://www.suttoncourtenay.org.uk/duncan/accu/pythonpatterns.html#singleton-and-the-borg
    """
    _instance = None

    def __new__(cls, *args, **kwargs):
        if not cls._instance:
            cls._instance = super(Singleton, cls).__new__(
                               cls, *args, **kwargs)
        return cls._instance

同样的方法也在这个问题中提到过:“有没有简单优雅的方法在Python中定义单例?

我通过子类化来使用单例模式:
class Settings(Singleton)
class Debug(Singleton)

最近我对程序做了一些修改,结果收到了这个警告:

/media/KINGSTON/Sumid/src/miscutil.py:39: DeprecationWarning: 
object.__new__() takes no parameters
cls._instance = super(Singleton, cls).__new__(cls, *args, **kwargs)

我找到了一些关于__new__被弃用的解释(是Guido说的),里面提到这些参数根本没有被使用。传递一个不需要的参数可能是个bug的表现。

所以我决定清除这些参数:

class Singleton(object):
_instance = None

def __new__(cls):
    if not cls._instance:
        cls._instance = super(Singleton, cls).__new__()
    return cls._instance

结果出现了以下异常:

Traceback (most recent call last):
 File "sumid.py", line 1168, in <module>
  settings = Settings()
 File "/media/KINGSTON/Sumid/src/miscutil.py", line 45, in __new__
  cls._instance = super(Singleton, cls).__new__()
 TypeError: object.__new__(): not enough arguments

当我把这一行改成cls._instance = super(Singleton, cls).__new__(cls)时,我得到了:

Traceback (most recent call last):
  File "sumid.py", line 1174, in <module>
    debug = Debug(settings)
TypeError: __new__() takes exactly 1 argument (2 given)

Gimmel建议了另一种解决方案_instance = type.__new__(cls)。对我来说,这样做破坏了继承关系:

Traceback (most recent call last):
  File "sumid.py", line 1168, in <module>
    settings = Settings()
  File "/media/KINGSTON/Sumid/src/miscutil.py", line 40, in __new__
    _instance = type.__new__(cls)
TypeError: type.__new__(Settings): Settings is not a subtype of type

同样的问题也出现在Menno Smits身上。但我不太理解建议的解决方案。而且我在相关代码中没有使用多重继承。

我没有尝试“在Python中有没有简单优雅的方法定义单例?”中的另一个例子,但一眼看过去,它可能会有同样的问题。

我在一个程序中使用单例模式,不想因为一个警告就完全重写它。因此,以下的回答对我没有帮助:

  • 单例是错的,根本不要用单例。
  • 用Borg代替,它更符合Python风格。
  • 用模块代替类。

最后我再重复一下问题:
如何调整单例模式,以考虑弃用警告,并对现有代码的影响最小化

编辑: 当子类的初始化需要一个参数时,cls._instance = object.__new__(cls)会引发TypeError:

class Child(Singleton):
    def __init__(self,param=None):
        print(param)
        print("Doing another stuff.")

ch = Child("Some stuff") 

3 个回答

0

我在一个关于Python 3的模式和习惯用法的资料中发现了这个模式。这肯定会对你有帮助。我很想知道它是否能解决你的问题,不过这可能和你问题中提到的“最小影响”条件有点冲突。

Python 3: 单例模式

1

根据@Sven和@Duncan的回答,我找到了一种适合我的解决方案。问题其实不在于引发TypeError的那行代码,而是在__new__()方法的定义上。调用object.__new__(cls)时,不应该带上*args和**kwargs,但在Singleton.__new__()的定义中,它们必须保留。这是修改后的Singleton:

class Singleton(object):
    """
    Singleton class by Duncan Booth.
    Multiple object variables refers to the same object.
    http://www.suttoncourtenay.org.uk/duncan/accu/pythonpatterns.html
    """
   _instance = None

    def __new__(cls, *args, **kwargs):
        if not cls._instance:
            cls._instance = super(Singleton, cls).__new__(cls)
        return cls._instance

这是一个子类化的例子(这就是问题所在):

class Child(Singleton):  
    def __init__(self,param=None):  
            print param  
            print 'Doing another stuff'  

ch=Child('Some stuff')

我还是不明白为什么子类的__init__()的定义必须和Singleton的new()匹配,但这个解决方案有效

10

你在创建对象的时候,需要去掉你传入的额外参数。把出问题的那一行改成:

        cls._instance = object.__new__(cls)

或者

        cls._instance = super(Singleton, cls).__new__(cls)

不过我觉得用第一种方式应该没问题(钻石继承和单例似乎不应该混在一起)。

附言:我试过这个建议,确实有效,所以我不知道你为什么不行。

根据@dragonx的评论进行编辑:正如评论中提到的,如果你传入了 *args, **kwargs,那么 object.__new__ 会抛出异常,所以对 __new__ 的 super 调用不应该包含除了 cls 以外的任何参数。这在原文写的时候并不是这样。当然,如果你选择基于其他类型,比如 tuple 来创建你的单例,那么你就需要传入合适的参数。

撰写回答