这个问题不是讨论singleton design pattern是否可取,是反模式,还是任何宗教战争,而是讨论如何以最pythonic的方式在Python中最好地实现这种模式 . 在这个例子中,我定义'most pythonic'意味着它遵循'principle of least astonishment' .
我有多个类可以成为单例(我的用例是 Logger ,但这并不重要) . 当我可以简单地继承或装饰时,我不希望在添加gumph的几个类中混乱 .
最好的方法:
方法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本身是一个函数,而不是类,因此您无法从中调用类方法 . 也适用于
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
优点
- 这是一个真正的课程
缺点
- 多重继承 - eugh!在从第二个基类继承期间,是否可以覆盖
__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__
的类 .
19 回答
这是我实现单身人士的首选方式:
这是我自己实现的单身人士 . 你所要做的就是装饰课程;要获得单例,您必须使用
Instance
方法 . 这是一个例子:这是代码:
我会把我扔到戒指里 . 这是一个简单的装饰 .
我认为它有一些其他解决方案的好处:
它清晰简洁(我的眼睛; D) .
它的动作是完全封装的 . 您无需更改
YourClass
实现的任何内容 . 这包括不需要为您的类使用元类(请注意,上面的元类是在工厂,而不是"real"类) .它不依赖猴子修补任何东西 .
这对来电者来说是透明的:
调用者仍然只是导入
YourClass
,它看起来像一个类(因为它),并且它们正常使用它 . 无需使呼叫者适应工厂功能 .YourClass()
实例化仍然是您实现的YourClass
的真实实例,而不是任何类型的代理,因此不会产生副作用 .isinstance(instance, YourClass)
和类似的操作仍然按预期工作(虽然这个位确实需要abc,因此排除了Python <2.6) .我的一个缺点确实发生了:真正类的classmethods和staticmethods不能通过隐藏它的工厂类来透明地调用 . 我从来没有遇到过这种需求,但是通过在工厂上使用自定义元类来实现
__getattr__()
来委托对真实类的全部属性访问,可以很容易地解决这个问题 .一个相关的模式I 've actually found more useful (not that I' m说这些事情经常是必需的)是一个"Unique"模式,其中使用相同的参数实例化类导致返回相同的实例 . 即a "singleton per arguments" . 以上内容适应这一点,变得更加简洁:
所有这一切,我同意一般的建议,如果你认为你需要其中一件事,你真的应该停下片刻,问自己是否真的这样做 . 99%的时间,YAGNI .
这个怎么样:
将它用作应该是单例的类的装饰器 . 像这样:
这与另一个答案中的
singleton = lambda c: c()
装饰器类似 . 与其他解决方案一样,唯一的实例具有类的名称(MySingleton
) . 但是,使用此解决方案,您仍然可以通过执行MySingleton()
从类中实际获得实例(实际上是唯一的实例) . 它还可以通过执行type(MySingleton)()
(也返回相同的实例)来阻止您创建其他实例 .它与fab的答案略有相似,但并不完全相同 .
单例合约不要求我们能够多次调用构造函数 . 由于单身人士应该只创造一次,不应该被视为仅创造一旦? "Spoofing"构造函数可能会损害易读性 .
所以我的建议是这样的:
这并不排除用户代码使用构造函数或字段
instance
:...如果你确定
Elvis
尚未创建,那King
已经 .但它鼓励用户普遍使用
the
方法:要完成此操作,您还可以覆盖
__delattr__()
以在尝试删除instance
时引发异常,并覆盖__del__()
以使其引发异常(除非我们知道程序正在结束...)进一步改进
感谢那些帮助评论和编辑的人,其中更受欢迎 . 虽然我使用Jython,但这应该更普遍,并且是线程安全的 .
注意事项:
如果你没有从python2.x中的对象继承,你会得到一个旧式的类,它不使用
__new__
装饰
__new__
时必须使用@classmethod进行装饰,否则__new__
将是未绑定的实例方法这可以通过使用元类来改进,因为这将允许您使
the
成为类级属性,可能将其重命名为instance
你可能永远不需要Python中的单例 . 只需在模块中定义所有数据和功能,即可拥有事实上的单例 .
如果你真的必须有一个单身课程,那么我会选择:
使用:
其中mysingleton.py是您定义My_Singleton的文件名 . 这是有效的,因为在第一次导入文件后,Python不会重新执行代码 .
这个答案很可能不是你想要的 . 我想要一个单身,因为只有那个对象有它的身份,以便与之比较 . 就我而言,它被用作Sentinel Value . 答案非常简单,使任何对象
mything = object()
和python的性质,只有那个东西才会有它的身份 .好吧,除了同意有关模块级全局的一般Pythonic建议,这个怎么样:
输出是:
我不记得我在哪里找到了这个解决方案,但我从非Python专家的角度来看它是最“优雅的”:
我为什么喜欢这个?没有装饰器,没有元类,没有多重继承...如果你决定不再让它成为Singleton,只需删除
__new__
方法 . 由于我是Python的新手(以及一般的OOP),我希望有人会直截了当地说明为什么这是一个糟糕的方法?这个解决方案在模块级别引起了一些命名空间污染(三个定义而不仅仅是一个),但我发现它很容易理解 .
我希望能够编写类似这样的内容(延迟初始化),但不幸的是,类在自己定义的主体中不可用 .
由于这是不可能的,我们可以打破初始化和静态实例
急切初始化:
延迟初始化:
急切初始化:
使用模块 . 它只导入一次 . 在其中定义一些全局变量 - 它们将是单例的'属性' . 添加一些函数 - 单例的'方法' .
模块只导入一次,其他一切都是过度思考 . 不要使用单例并尽量不使用全局变量 .
使用多种解决方案查看Stack Overflow问题Is there a simple, elegant way to define singletons in Python? .
我'd strongly recommend to watch Alex Martelli'在python中讨论设计模式:part 1和part 2 . 特别是,在第1部分中,他讨论了单身人士/共享状态对象 .
方法3似乎非常整洁,但如果您希望程序在Python 2和Python 3中运行,则它不起作用 . 即使使用Python版本的测试来保护单独的变体也会失败,因为Python 3版本在Python 2中提供了语法错误 .
感谢Mike Watkins:http://mikewatkins.ca/2008/11/29/python-2-and-3-metaclasses/ . 如果您希望程序在Python 2和Python 3中都能运行,则需要执行以下操作:
我认为赋值中的“对象”需要替换为“BaseClass”,但我没有尝试过(我已经尝试过代码如图所示) .
这是给你的单行:
这是你如何使用它:
您的对象急切地被实例化 . 这可能是也可能不是你想要的 .
以前的答案是正确的,但我不同意作为方法1下的问题的一部分发布的声明“MyClass本身是一个函数,而不是类,所以你不能调用类方法” . 请参阅下面的示例,在MyClass中使用singleton标记装饰多次调用该方法 .
另外,请注意这与发布的一些答案非常相似,并且基于python document,但它略有不同,因为类和函数的设计方式是它可以接收1或0个参数,但仍然是单例工作 .
这是证明您可以多次调用singleton的方法,并显示仍然使用该类的一个实例,并且不会创建新对象 .
由于theheadofabroom(发布问题的人)给出了一些关于他原始问题的反馈,然后我根据他的反馈开始研究新的解决方案(我仍然保留了我之前的答案,因为我认为它可能对某些人有用,即使它是不是100%正是heheadofabroom所要求的) . 这是更新的答案:
这是复制和粘贴它的代码:)
使用元类
我建议 Method #2 ,但你最好使用 metaclass 而不是基类 . 这是一个示例实现:
或者在Python3中
如果要在每次调用类时运行
__init__
,请添加到
Singleton.__call__
中的if
语句 .关于元类的几句话 . 元类是 class of a class ;也就是说,一个类是 instance of its metaclass . 您可以使用
type(obj)
在Python中找到对象的元类 . 普通的新式类的类型为type
. 上面代码中的Logger
将是class 'your_module.Singleton'
类型,就像Logger
的(唯一)实例将是class 'your_module.Logger'
类型一样 . 当您使用Logger()
调用logger时,Python首先会询问Logger
,Singleton
的元类,该怎么做,允许抢占实例创建 . 这个过程与Python通过执行myclass.attribute
引用其中一个属性时通过调用__getattr__
要求类做什么一样 .元类本质上决定 what the definition of a class means 以及如何实现该定义 . 参见例如http://code.activestate.com/recipes/498149/,它基本上使用元类在Python中重新创建C风格的
struct
. 线程What are your (concrete) use-cases for metaclasses in Python?也提供了一些示例,它们通常似乎与声明性编程有关,尤其是在ORM中使用 .在这种情况下,如果使用 Method #2 ,并且子类定义
__new__
方法,则它将 executed every time 调用SubClassOfSingleton()
- 因为它负责调用返回存储实例的方法 . 使用元类,当创建唯一实例时,它将 only be called once . 你想 customize what it means to call the class ,这是由它的类型决定的 .一般来说, makes sense 使用元类来实现单例 . 单例是特殊的,因为 created only once ,而元类是自定义 creation of a class 的方式 . 如果您需要以其他方式自定义单例类定义,则使用元类会为您提供 more control .
你的单身人士 won't need multiple inheritance (因为元类不是基类),但是对于使用多重继承的 subclasses of the created class ,你需要确保单例类是 first / leftmost ,其中一个元类重新定义
__call__
这不太可能是一个问题 . 实例字典是 not in the instance's namespace 所以它不会意外覆盖它 .您还会听到单例模式违反了"Single Responsibility Principle" - 每个类都应该 only one thing . 这样,如果您需要更改另一个代码,您不必担心代码会搞乱一件事,因为它们是分开的并且是封装的 . 元类实现 passes this test . 元类负责 enforcing the pattern ,创建的类和子类不必是 aware that they are singletons . Method #1 未通过此测试,正如您在"MyClass itself is a a function, not a class, so you cannot call class methods from it."中所述
Python 2和3兼容版本
编写适用于Python2和3的内容需要使用稍微复杂的方案 . 由于元类通常是
type
类型的子类,因此可以使用它来在运行时动态创建中间基类,并将其作为其元类,然后将其用作publicSingleton
基类的基类 . 这比解释更难解释,如下图所示:这种方法的一个具有讽刺意味的方面是它使用子类来实现元类 . 一个可能的优点是,与纯元类不同,
isinstance(inst, Singleton)
将返回True
.更正
在另一个主题上,您可能已经注意到了这一点,但原始帖子中的基类实现是错误的 .
_instances
需要 referenced on the class ,你需要使用super()
或者你是 recursing ,而__new__
实际上是一个静态方法,你必须 pass the class to ,而不是类方法,因为它被调用时的实际类 hasn't been created . 所有这些事情对于元类实现也是如此 .装饰师返回 class
我最初写的是评论但是时间太长了,所以我会在这里添加 . Method #4 比其他装饰器版本更好,但是's more code than needed for a singleton, and it'并不清楚它的作用 .
主要的问题源于这个类是什么's own base class. First, isn'如果一个类是一个几乎相同的类的子类,它的同名只存在于它的
__class__
属性中,这很奇怪吗?这也意味着您无法使用super()
定义 any methods that call the method of the same name on their base class ,因为它们会递归 . 这意味着您的类无法自定义__new__
,并且无法从需要__init__
调用它们的任何类派生 .何时使用单例模式
您的用例是 one of the better examples 想要使用单例 . 你在其中一条评论中说"To me logging has always seemed a natural candidate for Singletons."你是 absolutely right .
当人们说单身人士不好时,最常见的原因是他们是 implicit shared state . 虽然全局变量和顶级模块导入是 explicit 共享状态,但是传递的其他对象通常是实例化的 . 这是一个好点, with two exceptions .
第一个,也就是在各个地方被提到的一个,就是当单身人士是 constant 时 . 使用全局常量,特别是枚举,被广泛接受,并且无论如何都被认为是理智的, none of the users can mess them up for any other user . 对于常数单例也是如此 .
第二个例外,提到的更少,是相反的 - 当单例是 only a data sink 时,不是数据源(直接或间接) . 这就是为什么 Logger 感觉像是单身人士的用途 . 由于各种用户在其他用户关心的方式中都有 not changing the loggers ,因此有 not really shared state . 这否定了针对单例模式的主要论证,并使它们成为一个合理的选择,因为它们的任务是 ease of use .
以下是http://googletesting.blogspot.com/2008/08/root-cause-of-singletons.html的引用:
代码基于Tolli's answer .
说明:
创建新类,继承自给定的
cls
(它不会修改
cls
以防有人想要例如singleton(list)
)创建实例 . 在覆盖
__new__
之前,这很容易 .现在,当我们轻松创建实例时,使用之前定义的方法覆盖
__new__
.该函数仅在调用者期望的情况下返回
instance
,否则引发TypeError
.当有人试图从装饰类继承时,不满足条件 .
instance
已经初始化,因此函数替换__init__
,函数什么都不做 .See it working online
一个班轮(我不自豪,但它做的工作):