在Python中创build一个单例
这个问题不是讨论单例devise模式是否合意,是反模式还是任何宗教战争,而是讨论如何在python中最好的实现这种模式。 在这种情况下,我把“最pythonic”定义为遵循“最不惊讶的原则” 。
我有多个类将成为单身(我的用例是一个logging器,但这不是重要的)。 当我可以简单地inheritance或装饰时,我不希望杂乱地加上几个类似的东西。
最好的方法:
方法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
优点
- 装饰器是以一种比多重inheritance更直观的方式添加的。
缺点
- 虽然使用MyClass()创build的对象将是真正的单例对象,MyClass本身是一个函数,而不是一个类,所以你不能从它调用类的方法。 另外对于
m = MyClass(); n = MyClass(); o = type(n)();
m = MyClass(); n = MyClass(); o = type(n)();
那么m == n && m != o && n != o
方法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
优点
- 这是一个真正的阶级
缺点
- 多重inheritance – 呃! 在inheritance第二个基类的时候
__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
优点
- 这是一个真正的阶级
- 自动奇迹般地覆盖inheritance
- 为了正确的目的而使用
__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
优点
- 这是一个真正的阶级
- 自动奇迹般地覆盖inheritance
缺点
- 是否没有创build每个新class级的开销? 这里我们为每一个我们希望做单例的类创build两个类。 虽然这在我的情况是好的,我担心这可能不会缩放。 当然,这个模式是否太简单,是否存在争议呢?
-
_sealed
属性的要点是什么? - 不能使用
super()
在基类上调用同名的方法,因为它们会recursion。 这意味着你不能自定义__new__
,也不能为需要调用__init__
的类__new__
子类。
使用Metaclass
我会推荐方法2 ,但是最好使用元类而不是基类。 这是一个示例实现:
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] class Logger(object): __metaclass__ = Singleton
或者在Python3中
class Logger(metaclass=Singleton): pass
如果您想在每次调用类时运行__init__
,请添加
else: cls._instances[cls].__init__(*args, **kwargs)
到Singleton.__call__
的if
语句。
关于元类的几句话。 元类是一个类的类 ; 也就是说,一个类是它的元类的一个实例 。 您可以使用type(obj)
findPython中对象的元类。 普通的新式类是typestype
。 上面的代码中的Logger
的types是class 'your_module.Singleton'
,就像Logger
的(只)实例的types为class 'your_module.Logger'
。 当你用Logger()
调用logger时,Python首先会询问Logger
, Singleton
的元类,该怎么做,从而允许实例创build被预先占用。 这个过程与Python通过执行myclass.attribute
引用其中一个属性来调用__getattr__
要求的类相同。
元类本质上决定了一个类的定义是什么意思,以及如何实现这个定义。 例如参见http://code.activestate.com/recipes/498149/ ,它使用元类重新创build了Python中的C风格struct
。 线程什么是Python中的元类的具体用例? 还提供了一些示例,它们通常似乎与声明性编程有关,特别是在ORM中使用的。
在这种情况下,如果使用Method#2 ,并且子类定义了__new__
方法,则每次调用SubClassOfSingleton()
时都会执行它,因为它负责调用返回存储实例的方法。 使用元类时,仅在创build唯一实例时才会调用一次 。 你想定制调用这个类的意思 ,这是由它的types决定的。
一般来说,使用元类来实现单例是有意义的 。 单例是特殊的,因为只创build一次 ,而元类是您自定义类的创build的方式。 使用元类给你更多的控制 ,以防需要以其他方式定制单例类定义。
你的单例不需要多重inheritance (因为元类不是一个基类),但是对于使用多重inheritance的创build类的子类 ,你需要确保单例类是第一个/最左边的类,它重新定义了一个元类__call__
这不太可能成为一个问题。 实例字典不在实例的名称空间中,因此不会意外覆盖它。
你也会听说单身模式违反了“单一责任原则” – 每个class级只能做一件事 。 这样,如果您需要更改另一个代码,则不必担心会混淆代码所做的一件事情,因为它们是单独封装的。 元类实现通过了这个testing 。 元类负责执行模式 ,创build的类和子类不需要知道它们是单例 。 方法#1没有通过这个testing,正如你在“MyClass本身是一个函数,而不是一个类,所以你不能调用它的类方法”。
Python 2和3兼容版本
编写在Python2和Python3中工作的东西需要使用一个稍微复杂的scheme。 由于元类通常是typestype
子类,因此可以使用它在运行时dynamic创build中间基类,并将其作为元类,然后将其用作公共Singleton
基类的基类。 如下所示,这比解释要困难得多:
# works in Python 2 & 3 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
这种方法的一个讽刺的方面是它使用子类来实现元类。 一个可能的优点是,与纯元类不同, isinstance(inst, Singleton)
将返回True
。
更正
在另一个话题上,你可能已经注意到了这一点,但是原始文章中的基类实现是错误的。 _instances
需要在类上引用 ,你需要使用super()
或者你正在__new__
, __new__
实际上是一个静态方法,你必须传递类 ,而不是一个类方法,因为实际的类没有当它被调用时被创build 。 所有这些事情对于元类的实现也是如此。
class Singleton(object): _instances = {} def __new__(class_, *args, **kwargs): if class_ not in class_._instances: class_._instances[class_] = super(Singleton, class_).__new__(class_, *args, **kwargs) return class_._instances[class_] class MyClass(Singleton): pass c = MyClass()
修饰器返回一个类
我原本是在写评论,但时间太长了,所以我在这里添加。 方法#4比其他的装饰器版本更好,但它比单独的代码更多的代码,并不清楚它是什么。
主要的问题来自类是它自己的基类。 首先,把一个类作为一个几乎完全相同的类的子类,并且只存在于__class__
属性中,是不是很奇怪? 这也意味着你不能使用super()
方法定义任何在它们的基类中调用同名方法的方法,因为它们将被recursion。 这意味着你的类不能自定义__new__
,也不能从任何需要调用__init__
类派生出来。
何时使用单例模式
你的用例是想要使用单例的一个更好的例子 。 你在其中一个评论中说:“对我来说,伐木一直是单身人士的天然select。” 你是对的 。
当人们说单身人士不好时,最常见的原因是他们是隐性的共享状态 。 虽然全局variables和顶级模块导入是显式共享状态,但通常会传递其他对象。 这是一个很好的观点, 除了两个例外 。
第一个,也是在不同地方提到的,是单身的时候。 使用全局常量,特别是枚举,被广泛接受,并被认为是理智的,因为无论如何, 没有一个用户可以把它们搞乱 。 对于一个不变的单身人士来说也是如此。
第二个例外情况,比较less提到,是相反的 – 当单身人士只是一个数据汇 ,而不是数据源(直接或间接)。 这就是为什么伐木者感觉自己是单身的“自然”使用。 由于各种用户不会以其他用户所关心的方式来改变logging器 ,所以并不存在真正的共享状态 。 这就否定了反对单例模式的主要观点,并且使它们成为一个合理的select,因为它们易于使用 。
以下是http://googletesting.blogspot.com/2008/08/root-cause-of-singletons.html的引用:;
现在,有一种单身可以。 这是一个所有可访问对象都是不可变的单例。 如果所有的对象都是不可改变的,那么Singleton就没有全局状态,因为一切都是恒定的。 但是把这种单体变成易变的是很容易的,这是非常滑的。 因此,我也反对这些单身汉,不是因为他们不好,而是因为他们很容易变坏。 (作为一个便笺,Java枚举就是这种单例,只要你没有把枚举放在你的枚举中,你就可以,所以请不要。)
其他types的Singletons是半可接受的,它们不影响代码的执行,它们没有“副作用”。 logging是完美的例子。 它加载了单身人士和全球状态。 这是可以接受的(因为它不会伤害你),因为你的应用程序不会有任何不同的行为,不pipe给定的logging器是否被启用。 这里的信息以一种方式stream动:从您的应用程序到logging器。 即使logging器是全局状态,因为没有信息从logging器stream入你的应用程序,logging器是可以接受的。 如果你想让你的testing断言某些东西被logging了,你仍然应该注入你的logging器,但是一般来说,logging器尽pipe充满状态并不是有害的。
class Foo(object): pass some_global_variable = Foo()
模块只能导入一次,其他的都是过度的。 不要使用单例,并尽量不要使用全局variables。
使用一个模块。 它只import一次。 定义一些全局variables – 它们将是单例的“属性”。 添加一些函数 – 单例的“方法”。
你可能从来不需要Python中的单例。 只需在模块中定义所有的数据和函数,就可以实现一个事实上的单例。
如果你真的必须有一个单身课,那么我会去:
class My_Singleton(object): def foo(self): pass my_singleton = My_Singleton()
使用:
from mysingleton import my_singleton my_singleton.foo()
其中mysingleton.py是My_Singleton定义的文件名。这是因为在第一次导入文件之后,Python不会重新执行代码。
以下是您的一行:
singleton = lambda c: c()
以下是你如何使用它:
@singleton class wat(object): def __init__(self): self.x = 1 def get_x(self): return self.x assert wat.get_x() == 1
你的对象被急切地实例化。 这可能是也可能不是你想要的。
检查出堆栈溢出问题是否有一个简单,优雅的方式来定义Python中的单身人士? 有几个解决scheme。
我强烈build议观看Alex Martelli关于pythondevise模式的讨论: 第1 部分和第2部分 。 特别是在第一部分中,他谈到了单身/共享状态对象。
这是我自己实现的单身人士。 你所要做的就是装饰课堂。 得到单身人士,然后你必须使用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. 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. Limitations: The decorated class cannot be inherited from. """ 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)
这个怎么样:
def singleton(cls): instance=cls() cls.__new__ = cls.__call__= lambda cls: instance cls.__init__ = lambda self: None return instance
将它用作应该是单例的类的装饰器。 喜欢这个:
@singleton class MySingleton: #....
这与另一个答案中的singleton = lambda c: c()
装饰器类似。 像其他解决scheme一样,唯一的实例具有类的名称( MySingleton
)。 但是,通过这个解决scheme,您仍然可以通过执行MySingleton()
从该类“创build”实例(实际上是唯一的实例MySingleton()
。 它也阻止你通过type(MySingleton)()
创build额外的实例(也返回相同的实例)。
查看Google开发者日美国 – Alex Martelli的Pythondevise模式 。 创造模式是在14分38秒 。
方法3似乎非常整洁,但是如果你希望你的程序在Python 2和Python 3中都运行,那么它是行不通的。 即使使用针对Python版本的testing来保护单独的变体也会失败,因为Python 3版本在Python 2中给出了语法错误。
感谢Mike Watkins: http : //mikewatkins.ca/2008/11/29/python-2-and-3-metaclasses/ 。 如果你想让这个程序在Python 2和Python 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] MC = Singleton('MC', (object), {}) class MyClass(MC): pass # Code for the class implementation
我认为在作业中的'对象'需要被replace为'BaseClass',但我没有尝试过(我试过的代码如图所示)。
那么,除了同意Pythonic关于模块级全局的build议之外,这又如何呢?
def singleton(class_): class class_w(class_): _instance = None def __new__(class2, *args, **kwargs): if class_w._instance is None: class_w._instance = super(class_w, class2).__new__(class2, *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(object): def __init__(self, text): print text @classmethod def name(class_): print class_.__name__ x = MyClass(111) x.name() y = MyClass(222) print id(x) == id(y)
输出是:
111 # the __init__ is called only on the 1st time MyClass # the __name__ is preserved True # this is actually the same instance
我不记得我在哪里find这个解决scheme,但是从我的非Python专家的angular度来看,我觉得它是最“优雅”的:
class SomeSingleton(dict): __instance__ = None def __new__(cls, *args,**kwargs): if SomeSingleton.__instance__ is None: SomeSingleton.__instance__ = dict.__new__(cls) return SomeSingleton.__instance__ def __init__(self): pass def some_func(self,arg): pass
为什么我喜欢这个? 没有装饰器,没有元类,也没有多重inheritance……如果你决定不再需要它,那就删除__new__
方法。 由于我是Python新手(一般而言是OOP),我希望有人会直接指出为什么这是一个可怕的方法?
代码基于托利的答案 。
#decorator, modyfies new_cls def _singleton(new_cls): instance = new_cls() #2 def new(cls): if isinstance(instance, cls): #4 return instance else: raise TypeError("I can only return instance of {}, caller wanted {}".format(new_cls, cls)) new_cls.__new__ = new #3 new_cls.__init__ = lambda self: None #5 return new_cls #decorator, creates new class def singleton(cls): new_cls = type('singleton({})'.format(cls.__name__), (cls,), {} ) #1 return _singleton(new_cls) #metaclass def meta_singleton(name, bases, attrs): new_cls = type(name, bases, attrs) #1 return _singleton(new_cls)
说明:
-
创build新的类,从给定的
cls
inheritance
(它不会修改cls
,以防某人想要singleton(list)
) -
创build实例。 在重写
__new__
之前,这很容易。 - 现在,当我们已经很容易的创build实例的时候,使用刚才定义的方法覆盖
__new__
。 -
函数仅在调用者期望的
instance
才返回instance
,否则引发TypeError
。
有人试图从装饰类inheritance时,条件不满足。 -
如果
__new__()
返回一个cls
实例,则新实例的__init__()
方法将被调用,如__init__(self[, ...])
,其中self是新实例,其余参数与传递给__new__()
。instance
已经被初始化,所以函数用函数replace__init__
什么也不做。
看到它在线工作
这与晶圆厂的答案略有相似,但并不完全相同。
单体合同并不要求我们能够多次调用构造函数。 由于单身人士应该一次创造一次,不应该被看作只创build一次? “欺骗”施工人员可能会损害可读性。
所以我的build议是这样的:
class Elvis(): def __init__(self): if hasattr(self.__class__, 'instance'): raise Exception() self.__class__.instance = self # initialisation code... @staticmethod def the(): if hasattr(Elvis, 'instance'): return Elvis.instance return Elvis()
这并不排除用户代码使用构造函数或字段instance
:
if Elvis() is King.instance:
…如果你确实知道Elvis
还没有被创造出来,那么这个King
就是这样。
但它鼓励用户普遍使用the
方法:
Elvis.the().leave(Building.the())
为了使这个完成,你也可以重写__delattr__()
来引发exception,如果试图删除instance
,并且覆盖__del__()
这样就会引发exception(除非我们知道程序正在结束…)
进一步改进
感谢那些帮助过评论和编辑的人,更多的是欢迎。 While I use Jython, this should work more generally, and be thread-safe.
try: # This is jython-specific from synchronize import make_synchronized except ImportError: # This should work across different python implementations def make_synchronized(func): import threading func.__lock__ = threading.Lock() def synced_func(*args, **kws): with func.__lock__: return func(*args, **kws) return synced_func class Elvis(object): # NB must be subclass of object to use __new__ instance = None @classmethod @make_synchronized def __new__(cls, *args, **kwargs): if cls.instance is not None: raise Exception() cls.instance = object.__new__(cls, *args, **kwargs) return cls.instance def __init__(self): pass # initialisation code... @classmethod @make_synchronized def the(cls): if cls.instance is not None: return cls.instance return cls()
Points of note:
- If you don't subclass from object in python2.x you will get an old-style class, which does not use
__new__
- When decorating
__new__
you must decorate with @classmethod or__new__
will be an unbound instance method - This could possibly be improved by way of use of a metaclass, as this would allow you to make
the
a class-level property, possibly renaming it toinstance
I'll toss mine into the ring. It's a simple decorator.
from abc import ABC def singleton(real_cls): class SingletonFactory(ABC): instance = None def __new__(cls, *args, **kwargs): if not cls.instance: cls.instance = real_cls(*args, **kwargs) return cls.instance SingletonFactory.register(real_cls) return SingletonFactory # Usage @singleton class YourClass: ... # Your normal implementation, no special requirements.
Benefits I think it has over some of the other solutions:
- It's clear and concise (to my eye ;D).
- Its action is completely encapsulated. You don't need to change a single thing about the implementation of
YourClass
. This includes not needing to use a metaclass for your class (note that the metaclass above is on the factory, not the "real" class). - It doesn't rely on monkey-patching anything.
- It's transparent to callers:
- Callers still simply import
YourClass
, it looks like a class (because it is), and they use it normally. No need to adapt callers to a factory function. - What
YourClass()
instantiates is still a true instance of theYourClass
you implemented, not a proxy of any kind, so no chance of side effects resulting from that. -
isinstance(instance, YourClass)
and similar operations still work as expected (though this bit does require abc so precludes Python <2.6).
- Callers still simply import
One downside does occur to me: classmethods and staticmethods of the real class are not transparently callable via the factory class hiding it. I've used this rarely enough that I've never happen to run into that need, but it would be easily rectified by using a custom metaclass on the factory that implements __getattr__()
to delegate all-ish attribute access to the real class.
A related pattern I've actually found more useful (not that I'm saying these kinds of things are required very often at all) is a "Unique" pattern where instantiating the class with the same arguments results in getting back the same instance. Ie a "singleton per arguments". The above adapts to this well and becomes even more concise:
def unique(real_cls): class UniqueFactory(ABC): @functools.lru_cache(None) # Handy for 3.2+, but use any memoization decorator you like def __new__(cls, *args, **kwargs): return real_cls(*args, **kwargs) UniqueFactory.register(real_cls) return UniqueFactory
All that said, I do agree with the general advice that if you think you need one of these things, you really should probably stop for a moment and ask yourself if you really do. 99% of the time, YAGNI.
This is my preferred way of implementing singletons:
class Test(object): obj = None def __init__(self): if Test.obj is not None: raise Exception('A Test Singleton instance already exists') # Initialization code here @classmethod def get_instance(cls): if cls.obj is None: cls.obj = Test() return cls.obj @classmethod def custom_method(cls): obj = cls.get_instance() # Custom Code here
This answer is likely not what you're looking for. I wanted a singleton in the sense that only that object had its identity, for comparison to. In my case it was being used as a Sentinel Value . To which the answer is very simple, make any object mything = object()
and by python's nature, only that thing will have its identity.
#!python MyNone = object() # The singleton for item in my_list: if item is MyNone: # An Example identity comparison raise StopIteration
This solution causes some namespace pollution at the module level (three definitions rather than just one), but I find it easy to follow.
I'd like to be able to write something like this (lazy initialization), but unfortunately classes are not available in the body of their own definitions.
# wouldn't it be nice if we could do this? class Foo(object): instance = None def __new__(cls): if cls.instance is None: cls.instance = object() cls.instance.__class__ = Foo return cls.instance
Since that isn't possible, we can break out the initialization and the static instance in
Eager Initialization:
import random class FooMaker(object): def __init__(self, *args): self._count = random.random() self._args = args class Foo(object): def __new__(self): return foo_instance foo_instance = FooMaker() foo_instance.__class__ = Foo
Lazy initialization:
Eager Initialization:
import random class FooMaker(object): def __init__(self, *args): self._count = random.random() self._args = args class Foo(object): def __new__(self): global foo_instance if foo_instance is None: foo_instance = FooMaker() return foo_instance foo_instance = None