How to adapt the Singleton pattern? (Deprecation warning)
Few years ago I found an implementation of the Singleton pattern in Python by Duncan Booth:
class Sing开发者_如何学Cleton(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
The same approach is also described in question "Is there a simple, elegant way to define Singletons in Python?"
I use the Singleton via sub-classing:
class Settings(Singleton)
class Debug(Singleton)
Recently I made some changes to the program and got this warning:
/media/KINGSTON/Sumid/src/miscutil.py:39: DeprecationWarning:
object.__new__() takes no parameters
cls._instance = super(Singleton, cls).__new__(cls, *args, **kwargs)
I found explanation (by Guido) about __new__
deprecation which says that the parameters are not used at all. Passing an argument which is not needed can be symptom of a bug.
So I decided to clear the parameters:
class Singleton(object):
_instance = None
def __new__(cls):
if not cls._instance:
cls._instance = super(Singleton, cls).__new__()
return cls._instance
Which resulted in following exception:
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
When I modify the line to cls._instance = super(Singleton, cls).__new__(cls)
, I'll get:
Traceback (most recent call last):
File "sumid.py", line 1174, in <module>
debug = Debug(settings)
TypeError: __new__() takes exactly 1 argument (2 given)
Gimmel suggest another solution: _instance = type.__new__(cls)
. For me it breaks the inheritance:
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
The same problem has also Menno Smits. But I don't understand the solution suggested. Moreover I have no multiple inheritance in relevant code.
I didn't try the another example in "Is there a simple, elegant way to define Singletons in Python?", but at a glance it probably will have the same problem.
I use the Singleton pattern in a program and I don't want to rewrite it completely just because one warning. Thus following answers wont help me:
- Singleton is wrong. Don't use the Singleton at all.
- Use Borg instead, it's more pythonic.
- Use module instead of a class.
To conclude I'll repeat the question:
How to adapt the Singleton pattern with consideration of the deprecation warning with the minimal impact to existing code?Edit:
Line cls._instance = object.__new__(cls)
raises the TypeError when the Child's init takes an argument:
class Child(Singleton):
def __init__(self,param=None):
print(param)
print("Doing another stuff.")
ch = Child("Some stuff")
You need to drop any additional arguments you are passing when you construct the object. Change the offending line to:
cls._instance = object.__new__(cls)
or
cls._instance = super(Singleton, cls).__new__(cls)
though I think you'll be fine with the first (diamond inheritance and singletons sound as though they shouldn't be mixed).
P.S. I did try this suggestion and it works for me so I don't know why it didn't work for you.
Edit in response to @dragonx's comment: As pointed out in the comments, object.__new__
will throw an exception if you pass on *args, **kwargs
so the super call to __new__
should not include any arguments apart from cls
. This wasn't the case when the original article was written. Also of course if you choose to base your singleton on some other type such as a tuple
you would then need to pass the appropriate arguments.
So based on the answers from @Sven and @Duncan I found a solution which works for me. The problem actually wasn't in the line of the code raising the TypeError, but in the signature of the __new__()
method. The call of the object.__new__(cls)
shall be without the *args, **kwargs, but they have to remain in the Singleton.__new__()
definition. This is the modified 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
And this is an example of sub-classing (which was the issue):
class Child(Singleton):
def __init__(self,param=None):
print param
print 'Doing another stuff'
ch=Child('Some stuff')
I still don't understand why the signature of Child's __init__()
has to match to Singleton's `new(), but this solution works.
I found this pattern described in a Python 3 pattern and idioms source. This should certainly help. I would love to know if it solves your problem, although it may violate the minimal impact condition in your question.
Python 3: The Singleton
精彩评论