i need to write a singleton so i looked up some examples. here's one of them from
https://stackoverflow.com/questions/6760...on#6798042
class _Singleton(type):
""" A metaclass that creates a Singleton base class when called. """
_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]
class Singleton(_Singleton('SingletonMeta', (object,), {})): pass
class Logger(Singleton):
pass
i've just read about metaclasses but i don't understand how the above example uses a metaclass. shouldn't it be? -->
class _Singleton(type):
""" A metaclass that creates a Singleton base class when called. """
_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]
class Singleton(metaclass=_Singleton('SingletonMeta', (object,), {})): pass # notice the metaclass
class Logger(Singleton):
pass
however i still want isinstance(inst, Singleton) to be True.
(Feb-10-2018, 03:27 PM)bb8 Wrote: [ -> ]shouldn't it be? -->
No. The first version is correct. It says that the parent class of
Singleton
is an *instance* of
_Singleton
. It follows that all subclasses of
Singleton
will have the
__call__()
method of
_Singleton
(when it is called on the class, eg Logger(), not on instances of Logger)
Note that the author of this code wrote it only to ensure compatibility with python 2. A simpler version works on python 3 only:
class Singleton(metaclass=_Singleton):
pass
Edit: I think your first version can be replaced by something simpler:
Singleton = _Singleton('Singleton', (object,), {})
what is the difference between your last code snippet and declaring the class?
The difference is that it works also in python 2!
The code can be improved. In the following snippet, I add some features
- The
_instances
dictionary is written appart from the class, so that it cannot be accessed through subclasses of Singleton
- Direct subclasses of
Singleton
are final (cannot be subclassed). This is necessary to preserve the unique instance paradigm: if one could create a Logger subclass named FooLogger, it would create two instances of Logger because a FooLogger is a Logger.
- The base
Singleton
class cannot be instanciated.
_instances = {}
class _Singleton(type):
""" A metaclass that creates a Singleton base class when called. """
def __new__(meta, name, bases, members):
"""Creates a new _Singleton instance (a singleton type)
This function allows creation of a single base class named Singleton
and direct subclasses of Singleton. Attempts to subclass one of these
subclasses raise a TypeError
"""
for b in bases:
if b is not Singleton and issubclass(b, Singleton):
raise TypeError(('Invalid base type (as subclass of Singleton):', b))
return type.__new__(meta, name, bases, members)
def __call__(cls, *args, **kwargs):
if cls is Singleton:
raise TypeError('The base Singleton type cannot be instanciated')
if cls not in _instances:
_instances[cls] = super(_Singleton, cls).__call__(*args, **kwargs)
return _instances[cls]
Singleton = type('_',(),{}) # dummy type to allow base type creation
Singleton = _Singleton('Singleton', (object,), {})
if __name__ == '__main__':
class A(Singleton):
pass
a, aa = A(), A()
assert a is aa
Notice that now the following raises TypeError:
class B(A):
pass
thanks
wouldn't __new__() get called every time a class a tried to be instantiated?
(Feb-11-2018, 06:34 PM)bb8 Wrote: [ -> ]wouldn't __new__() get called every time a class a tried to be instantiated?
No,
__new__
is called every time an instance of the metaclass
_Singleton
is created. It means that it will be called every time a subclass of
Singleton
is defined, but not when such classes try to be instantiated. You can check this easily by adding a print statement in
__new__()
okay
and i get this error:
TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
here's the class:
class MyAppClass(QObject, Singleton):
(Feb-12-2018, 04:43 PM)bb8 Wrote: [ -> ]and i get this error:
It means that QObject already has a metaclass and that python cannot subclass two classes having different metaclasses unless you define the subclass with a metaclass that is a common subclass of both metaclasses. It won't work with the singletons we defined above.
The pythonic solution is to forget about the Singleton and ensure by other means that there can't be more than one instance of MyAppClass.
oh got it now...
what are the other ways?
i've seen people using some list or dictionary to keep track of the instance. like that?
Why do you need anything to keep track of a single instance? You only need this:
class MyApp(QObject):
pass
myapp = MyApp()