在 Python 中创建单例Python 2 和 3 兼容版本
这个问题不是为了讨论单例设计模式是否是可取的,是否是一种反模式,或者用于任何宗教战争,而是讨论如何以最pythonic的方式在Python中最好地实现这种模式。在这个例子中,我将“最pythonic”定义为它遵循“最小惊讶原则”。
我有多个类会变成单例(我的用例是记录器,但这并不重要)。我不希望在我可以简单地继承或装饰时用额外的口臭来混乱几个类。
最佳方法:
方法 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
优点
- 装饰器以一种通常比多重继承更直观的方式进行加法。
缺点
-
虽然使用 创建的对象是真正的单例对象,但其本身是一个函数,而不是一个类,因此您无法从中调用类方法。也适用于
MyClass()
MyClass
x = MyClass(); y = MyClass(); t = type(n)();
然后但是x == y
x != t && y != t
方法 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
优点
- 这是一个真正的类
缺点
- 多重继承 - 哎呀! 在从第二个基类继承期间可能会被覆盖吗?人们必须考虑不必要的事情。
__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
优点
- 这是一个真正的类
- 自动神奇地覆盖继承
- 用于其正确目的(并让我意识到这一点)
__metaclass__
缺点
- 有吗?
方法 4:装饰器返回具有相同名称的类
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
优点
- 这是一个真正的类
- 自动神奇地覆盖继承
缺点
- 创建每个新类没有开销吗?在这里,我们为希望创建单例的每个类创建两个类。虽然这在我的情况下很好,但我担心这可能无法扩展。当然,关于这种模式是否太容易衡量,存在争议......
- 属性的要点是什么
_sealed
- 不能在 基类上使用调用同名的方法,因为它们将递归。这意味着您无法自定义也无法子类化需要调用 的类。
super()
__new__
__init__
方法 5:模块
模块文件singleton.py
优点
- 简单胜于复杂
缺点
- 未延迟实例化