在Python中,似乎有许多定义单例的方法。在Stack Overflow上是否有共识意见?
在Python中,似乎有许多定义单例的方法。在Stack Overflow上是否有共识意见?
我并不觉得有必要使用单例模式,因为只包含函数(而非类)的模块可以很好地充当单例。所有变量都将绑定到模块,无论如何也无法重复实例化。
如果您确实希望使用类,则在Python中没有创建私有类或私有构造函数的方法,因此除了通过API的惯例使用保护措施外,无法防止多次实例化。我仍然会将方法放在一个模块中,并将该模块视为单例。
这是我自己实现的单例模式。您只需要给类添加装饰器即可,要获取单例,您需要使用 Instance
方法。以下是一个示例:
@Singleton
class Foo:
def __init__(self):
print 'Foo created'
f = Foo() # Error, this isn't how you get the instance of a singleton
f = Foo.instance() # Good. Being explicit is in line with the Python Zen
g = Foo.instance() # Returns already created instance
print f is g # True
以下是代码:
class Singleton:
"""
A non-thread-safe helper class to ease implementing singletons.
This should be used as a decorator -- not a metaclass -- to the
class that should be a singleton.
The decorated class can define one `__init__` function that
takes only the `self` argument. Also, the decorated class cannot be
inherited from. Other than that, there are no restrictions that apply
to the decorated class.
To get the singleton instance, use the `instance` method. Trying
to use `__call__` will result in a `TypeError` being raised.
"""
def __init__(self, decorated):
self._decorated = decorated
def instance(self):
"""
Returns the singleton instance. Upon its first call, it creates a
new instance of the decorated class and calls its `__init__` method.
On all subsequent calls, the already created instance is returned.
"""
try:
return self._instance
except AttributeError:
self._instance = self._decorated()
return self._instance
def __call__(self):
raise TypeError('Singletons must be accessed through `instance()`.')
def __instancecheck__(self, inst):
return isinstance(inst, self._decorated)
initialize()
方法,可以接受任何参数,并在第二次调用时抛出异常。 - Paul Mantafunctools.wraps
或functools.update_wrapper
。其次,必须通过调用Foo.Instance()
来获取实例,这是非常不符合Python风格的。而且完全可以像这样实现Foo()
。第三,替换类会产生意外的结果,比如type(Foo.instance()) is Foo
-> False
。 - Aran-Fey您可以通过以下方法覆盖 __new__
方法:
class Singleton(object):
_instance = None
def __new__(cls, *args, **kwargs):
if not cls._instance:
cls._instance = super(Singleton, cls).__new__(
cls, *args, **kwargs)
return cls._instance
if __name__ == '__main__':
s1 = Singleton()
s2 = Singleton()
if (id(s1) == id(s2)):
print "Same"
else:
print "Different"
singleton.py:9: DeprecationWarning: object.__new__() takes no parameters
cls._instance = super(Singleton,cls).__new__(cls,*args,**kwargs)
- Siddhant_instance
(但反之则不然)。为了解决这个问题,您可以将 if not cls._instance
替换为 if type(cls._instance) != cls
(这样做是因为 None
不是 Singleton
,而 Singleton
不是 SubSingleton(Singleton)
)。 - rlat在Python中实现单例的一种稍微不同的方法是borg模式,由Alex Martelli(Google员工和Python天才)提出。
class Borg:
__shared_state = {}
def __init__(self):
self.__dict__ = self.__shared_state
所以,与其强制所有实例具有相同的标识,它们共享状态。
模块化方法效果良好。如果我绝对需要一个单例,我更喜欢元类的方法。
class Singleton(type):
def __init__(cls, name, bases, dict):
super(Singleton, cls).__init__(name, bases, dict)
cls.instance = None
def __call__(cls,*args,**kw):
if cls.instance is None:
cls.instance = super(Singleton, cls).__call__(*args, **kw)
return cls.instance
class MyClass(object):
__metaclass__ = Singleton
__deepcopy__()
方法来解决? - martineautype.__init__
,而不是MyClass.__init__
。 - Ericclass SingletonMeta(type):
def __new__(cls, name, bases, dict):
dict['__deepcopy__'] = dict['__copy__'] = lambda self, *args: self
return super(SingletonMeta, cls).__new__(cls, name, bases, dict)
看看这个实现来自PEP318,使用装饰器实现单例模式:
def singleton(cls):
instances = {}
def getinstance():
if cls not in instances:
instances[cls] = cls()
return instances[cls]
return getinstance
@singleton
class MyClass:
...
@singleton
class MyClass(BaseClass):
def __init__(self):
super(MyClass, self).__init__()
- mkmPython文档有涉及到这个问题:
class Singleton(object):
def __new__(cls, *args, **kwds):
it = cls.__dict__.get("__it__")
if it is not None:
return it
cls.__it__ = it = object.__new__(cls)
it.init(*args, **kwds)
return it
def init(self, *args, **kwds):
pass
我可能会将它重写为更像这样的形式:
class Singleton(object):
"""Use to create a singleton"""
def __new__(cls, *args, **kwds):
"""
>>> s = Singleton()
>>> p = Singleton()
>>> id(s) == id(p)
True
"""
it_id = "__it__"
# getattr will dip into base classes, so __dict__ must be used
it = cls.__dict__.get(it_id, None)
if it is not None:
return it
it = object.__new__(cls)
setattr(cls, it_id, it)
it.init(*args, **kwds)
return it
def init(self, *args, **kwds):
pass
class A(Singleton):
pass
class B(Singleton):
pass
class C(A):
pass
assert A() is A()
assert B() is B()
assert C() is C()
assert A() is not B()
assert C() is not B()
assert C() is not A()
扩展这个应该相对清晰简单:
class Bus(Singleton):
def init(self, label=None, *args, **kwds):
self.label = label
self.channels = [Channel("system"), Channel("app")]
...
__new__
中不应使用hasattr
和getattr
,因为它们都调用object.__getattribute__
,后者通过整个类层次结构查找您的"__self__"
属性,而不仅仅是当前类。如果Guido使用__dict__
进行属性访问,那肯定有他的原因。试试:class A(GuidoSingleton): pass
, class B(A): pass
, class C(YourSingleton): pass
, class D(C): pass
, print(A(), B(), C(), D())
。所有子类都引用同一个YourSingleton
实例! - Géry OgamTypeError: __init __()仅接受1个位置参数,但提供了4个。
唯一不同的是使用了dataclasses。 - Zhanwen Chen我非常不确定,但我的项目使用“惯例单例”(而不是强制单例),也就是说,如果我有一个名为DataController
的类,我会在同一模块中定义它:
_data_controller = None
def GetDataController():
global _data_controller
if _data_controller is None:
_data_controller = DataController()
return _data_controller
虽然这不是很优雅,因为它有完整的六行。但我的所有单例都使用这种模式,它至少非常明确(这是Pythonic的)。
DataController
应该改为 _DataController
吗?否则可以直接实例化它。 - nosdef singleton(cls):
obj = cls()
# Always return the same object
cls.__new__ = staticmethod(lambda cls: obj)
# Disable __init__
try:
del cls.__init__
except AttributeError:
pass
return cls
请查看Python数据模型,以获取有关__new__
的更多详细信息。
示例:
@singleton
class Duck(object):
pass
if Duck() is Duck():
print "It works!"
else:
print "It doesn't work!"
注意事项:
你必须使用新式类(从object
派生)来实现此功能。
单例在定义时就被初始化,而不是在第一次使用时。
这只是一个玩具示例。我从未在生产代码中使用过它,并且也没有计划这样做。
我曾经在Python中编写单例时使用了一个类,其中所有成员函数都带有classmethod修饰符。
class Foo:
x = 1
@classmethod
def increment(cls, y=1):
cls.x += y
__len__
或__getitem__
等方法作为类方法使用,因此你没有像使用对象那样灵活自定义的能力。由于我经常想将单例用作数据集合,所以这有点令人失望。 - Dan Homerickimport this
)——只是这种方法不过是简单明了,并且似乎非常接近使用全局变量,而这通常被认为是一种糟糕的工程实践。 - martineau__len__
,__getitem__
甚至@property
,您可以使用__metaclass__
设置为定义上述内容的类。这很有效。我建议将类设置为单例模式,因为它是元类的实例,符合语言设计。实际上,所有方法都可以在元类中定义,然后类将仅用作对单例的引用。 - Leonid Usov