在Python中实现单例的最佳方式是什么
这个问题不是为了讨论单例设计模式是否可取,是一种反模式,或者针对任何宗教战争,但讨论如何以最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 = 类型(n)();
then 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
优点 简单
- 胜于复杂
缺点
- 不延迟实例化
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(30)
使用元类
我建议使用方法#2,但是使用元类比使用基类更好。下面是一个示例实现:
或者在 Python3 中,
如果您希望每次调用类时都运行 __init__,请添加
到
Singleton.__call__
中的if
语句代码>.关于元类的几句话。元类是类的类;也就是说,一个类是其元类的一个实例。您可以使用
type(obj)
在 Python 中查找对象的元类。普通的新式类的类型是type
。上面代码中的Logger
的类型为class 'your_module.Singleton'
,就像Logger
的(唯一)实例的类型一样类“your_module.Logger”
。当你用Logger()
调用logger时,Python首先询问Logger
的元类Singleton
要做什么,允许实例创建预先完成-清空。此过程与当您通过执行myclass.attribute
引用某个类的属性时,Python 通过调用__getattr__
来询问类要做什么。元类本质上决定类的定义意味着什么以及如何实现该定义。例如,请参见 http://code.activestate.com/recipes/498149/,本质上是使用元类在 Python 中重新创建 C 风格的结构。线程什么是一些(具体)用例元类? 还提供了一些示例,它们通常似乎与声明式编程相关,尤其是在 ORM 中使用的。
在这种情况下,如果您使用方法#2,并且子类定义了
__new__
方法,则每次调用时都会执行< code>SubClassOfSingleton()——因为它负责调用返回存储实例的方法。对于元类,它仅在创建唯一实例时被调用一次。您想要自定义调用类的含义,这是由其类型决定的。一般来说,使用元类来实现单例是有意义的。单例很特殊,因为它的实例仅创建一次,而元类是您自定义创建类的方式,允许它的行为与普通类不同。如果您需要以其他方式自定义单例类定义,使用元类可以为您提供更多控制。
您的单例不需要多重继承(因为元类不是基类),但对于使用多重继承的创建的类的子类,您需要确保单例类是第一个/最左边的一个,其元类重新定义了
__call__
,这不太可能成为问题。实例字典不在实例的命名空间中,因此不会意外覆盖它。您还会听说单例模式违反了“单一职责原则”——每个类应该只做一件事。这样,如果您需要更改代码所做的一件事,就不必担心弄乱另一件事,因为它们是独立且封装的。元类实现通过了此测试。元类负责强制执行模式,并且创建的类和子类不需要知道它们是单例。 方法 #1 未通过此测试,正如您在“MyClass 本身是一个函数,而不是一个类,因此您无法从中调用类方法”中所指出的那样。
Python 2 和 3 兼容版本
编写在 Python2 和 3 中都可以工作的东西需要使用稍微复杂的方案。由于元类通常是
type
类型的子类,因此可以使用元类在运行时动态创建一个中间基类,并将其作为元类,然后使用that作为基类公共Singleton
基类的。解释起来比做起来更难,如下所示:这种方法的一个讽刺之处在于它使用子类化来实现元类。一个可能的优点是,与纯元类不同,
isinstance(inst, Singleton)
将返回True
。更正在
另一个主题上,您可能已经注意到了这一点,但是原始帖子中的基类实现是错误的。
_instances
需要在类上引用,您需要使用super()
或者您递归,并且__new__
实际上是一个静态方法,您必须将类传递给它,而不是类方法,因为实际的类尚未创建 > 然而当它被调用时。所有这些事情对于元类实现也同样适用。装饰器返回一个类
我本来是在写评论,但它太长了,所以我会在这里添加它。 方法 #4 比其他装饰器版本更好,但它的代码比单例所需的代码要多,并且不清楚它的作用。
主要问题源于该类是它自己的基类。首先,让一个类成为一个几乎相同的类的子类,并且该类的同名仅存在于其
__class__
属性中,这不是很奇怪吗?这也意味着您不能使用super()
定义任何在其基类上调用同名方法的方法,因为它们会递归。这意味着您的类无法自定义__new__
,也无法从任何需要调用__init__
的类派生。何时使用单例模式
您的用例是想要使用单例的更好的示例之一。您在其中一条评论中说“对我来说,日志记录似乎一直是单例的自然候选者。”你完全正确。
当人们说单例不好时,最常见的原因是它们是隐式共享状态。虽然全局变量和顶级模块导入是显式共享状态,但传递的其他对象通常会被实例化。这是一个很好的观点,有两个例外。
第一个,也是在很多地方提到的,是当单例恒定时。全局常量(尤其是枚举)的使用被广泛接受,并被认为是明智的,因为无论如何,没有一个用户可以为任何其他用户搞乱它们。对于常量单例来说也是如此。
第二个例外(较少提及)是相反的——当单例只是一个数据接收器,而不是数据源(直接或间接)时。这就是为什么记录器感觉像是单例的“自然”用途。由于各个用户没有以其他用户关心的方式更改记录器,因此存在未真正共享的状态。这否定了反对单例模式的主要论点,并使它们成为合理的选择,因为它们易于使用来完成任务。
以下是 http://googletesting.blogspot.com/ 的引用2008/08/root-cause-of-singletons.html:
Use a Metaclass
I would recommend Method #2, but you're better off using a metaclass than a base class. Here is a sample implementation:
Or in Python3
If you want to run
__init__
every time the class is called, addto the
if
statement inSingleton.__call__
.A few words about metaclasses. A metaclass is the class of a class; that is, a class is an instance of its metaclass. You find the metaclass of an object in Python with
type(obj)
. Normal new-style classes are of typetype
.Logger
in the code above will be of typeclass 'your_module.Singleton'
, just as the (only) instance ofLogger
will be of typeclass 'your_module.Logger'
. When you call logger withLogger()
, Python first asks the metaclass ofLogger
,Singleton
, what to do, allowing instance creation to be pre-empted. This process is the same as Python asking a class what to do by calling__getattr__
when you reference one of its attributes by doingmyclass.attribute
.A metaclass essentially decides what the definition of a class means and how to implement that definition. See for example http://code.activestate.com/recipes/498149/, which essentially recreates C-style
struct
s in Python using metaclasses. The thread What are some (concrete) use-cases for metaclasses? also provides some examples, they generally seem to be related to declarative programming, especially as used in ORMs.In this situation, if you use your Method #2, and a subclass defines a
__new__
method, it will be executed every time you callSubClassOfSingleton()
-- because it is responsible for calling the method that returns the stored instance. With a metaclass, it will only be called once, when the only instance is created. You want to customize what it means to call the class, which is decided by its type.In general, it makes sense to use a metaclass to implement a singleton. A singleton is special because its instance is created only once, and a metaclass is the way you customize the creation of a class, allowing it to behave differenly than a normal class. Using a metaclass gives you more control in case you need to customize the singleton class definitions in other ways.
Your singletons won't need multiple inheritance (because the metaclass is not a base class), but for subclasses of the created class that use multiple inheritance, you need to make sure the singleton class is the first / leftmost one with a metaclass that redefines
__call__
This is very unlikely to be an issue. The instance dict is not in the instance's namespace so it won't accidentally overwrite it.You will also hear that the singleton pattern violates the "Single Responsibility Principle" -- each class should do only one thing. That way you don't have to worry about messing up one thing the code does if you need to change another, because they are separate and encapsulated. The metaclass implementation passes this test. The metaclass is responsible for enforcing the pattern and the created class and subclasses need not be aware that they are singletons. Method #1 fails this test, as you noted with "MyClass itself is a a function, not a class, so you cannot call class methods from it."
Python 2 and 3 Compatible Version
Writing something that works in both Python2 and 3 requires using a slightly more complicated scheme. Since metaclasses are usually subclasses of type
type
, it's possible to use one to dynamically create an intermediary base class at run time with it as its metaclass and then use that as the baseclass of the publicSingleton
base class. It's harder to explain than to do, as illustrated next:An ironic aspect of this approach is that it's using subclassing to implement a metaclass. One possible advantage is that, unlike with a pure metaclass,
isinstance(inst, Singleton)
will returnTrue
.Corrections
On another topic, you've probably already noticed this, but the base class implementation in your original post is wrong.
_instances
needs to be referenced on the class, you need to usesuper()
or you're recursing, and__new__
is actually a static method that you have to pass the class to, not a class method, as the actual class hasn't been created yet when it is called. All of these things will be true for a metaclass implementation as well.Decorator Returning A Class
I originally was writing a comment but it was too long, so I'll add this here. Method #4 is better than the other decorator version, but it's more code than needed for a singleton, and it's not as clear what it does.
The main problems stem from the class being its own base class. First, isn't it weird to have a class be a subclass of a nearly identical class with the same name that exists only in its
__class__
attribute? This also means that you can't define any methods that call the method of the same name on their base class withsuper()
because they will recurse. This means your class can't customize__new__
, and can't derive from any classes that need__init__
called on them.When to use the singleton pattern
Your use case is one of the better examples of wanting to use a singleton. You say in one of the comments "To me logging has always seemed a natural candidate for Singletons." You're absolutely right.
When people say singletons are bad, the most common reason is they are implicit shared state. While with global variables and top-level module imports are explicit shared state, other objects that are passed around are generally instantiated. This is a good point, with two exceptions.
The first, and one that gets mentioned in various places, is when the singletons are constant. Use of global constants, especially enums, is widely accepted, and considered sane because no matter what, none of the users can mess them up for any other user. This is equally true for a constant singleton.
The second exception, which get mentioned less, is the opposite -- when the singleton is only a data sink, not a data source (directly or indirectly). This is why loggers feel like a "natural" use for singletons. As the various users are not changing the loggers in ways other users will care about, there is not really shared state. This negates the primary argument against the singleton pattern, and makes them a reasonable choice because of their ease of use for the task.
Here is a quote from http://googletesting.blogspot.com/2008/08/root-cause-of-singletons.html:
模块只导入一次,其他的都是想多了。不要使用单例,也尽量不要使用全局变量。
Modules are imported only once, everything else is overthinking. Don't use singletons and try not to use globals.
使用模块。它仅导入一次。在其中定义一些全局变量 - 它们将是单例的“属性”。添加一些函数 - 单例的“方法”。
Use a module. It is imported only once. Define some global variables in it - they will be singleton's 'attributes'. Add some functions - the singleton's 'methods'.
在 Python 中你可能永远不需要单例。只需在模块中定义所有数据和函数,您就有了一个事实上的单例:
使用:
如果您确实必须有一个单例类,那么我会选择:
使用:
where
mysingleton.py 是定义
MySingleton
的文件名。这是有效的,因为第一次导入文件后,Python 不会重新执行代码。You probably never need a singleton in Python. Just define all your data and functions in a module and you have a de facto singleton:
To use:
If you really absolutely have to have a singleton class then I'd go with:
To use:
where
mysingleton.py
is your filename thatMySingleton
is defined in. This works because after the first time a file is imported, Python doesn't re-execute the code.你只需要一个装饰器,具体取决于Python版本:
Python 3.2+
实现
用法
输出
Python 3.9+
Implementation:
You just need a decorator, depending on the python version:
Python 3.2+
Implementation
Usage
Output
Python 3.9+
Implementation:
这是给您的一句俏皮话:
以下是您如何使用它:
您的对象被急切地实例化。这可能是也可能不是您想要的。
Here's a one-liner for you:
Here's how you use it:
Your object gets instantiated eagerly. This may or may not be what you want.
装饰器
)。串行
通信的类,并且要创建一个想要将串行端口作为参数发送的实例,那么使用传统方法将不起作用decorators
).serial
communication, and to create an instance you want to send the serial port as an argument, then with traditional approach won't work使用函数属性也很简单
Using a function attribute is also very simple
我将推荐一种使用元类的优雅解决方案
输出:
正如您从输出中看到的,只有一个对象被实例化
I will recommend an elegant solution using metaclasses
Output:
As you can see from the output, only one object is instantiated
我更喜欢这个解决方案,我发现它非常清晰和直接。
例如,它正在使用双重检查,如果其他线程已经创建了它。
需要考虑的其他事情是确保反序列化不会创建任何其他实例。
https://gist.github.com/werediver/4396488
I prefer this solution which I found very clear and straightforward.
It is using double check for instance, if some other thread already created it.
Additional thing to consider is to make sure that deserialization isn't creating any other instances.
https://gist.github.com/werediver/4396488
一种线性解决方案:
将其添加到任何 init self.__class__.__new__ = lambda _: self
优点
__init__
中添加一行它是如何工作的?
__new__
在第一次调用__init__
后被覆盖,因此构造函数本质上是一次性的。 lambda 用于使用闭包返回对 self 的引用,这使得一切都非常匿名。One liner solution:
Add this to any init
self.__class__.__new__ = lambda _: self
Pros
__init__
How does it work?
__new__
is overwritten after the first call to__init__
so the constructor is essentially single-use. The lambda is used to return a reference toself
using a closure which makes everything very anonymous.这是我自己的单例实现。你所要做的就是装饰班级;要获取单例,您必须使用
Instance
方法。这是一个例子:这是代码:
Here's my own implementation of singletons. All you have to do is decorate the class; to get the singleton, you then have to use the
Instance
method. Here's an example:And here's the code:
使用类变量(无装饰器)
通过重写 __new__ 方法来返回类的相同实例。仅在第一次初始化类时的布尔值:
Use a class variable (no decorator)
By overriding the
__new__
method to return the same instance of the class. A boolean to only initialize the class for the first time:简单又有效!
Dead easy and works!
方法 3 似乎非常简洁,但是如果您希望程序在 Python 2 中运行 和 Python 3,它不起作用。即使使用 Python 版本的测试来保护单独的变体也会失败,因为 Python 3 版本在 Python 2 中给出了语法错误。
感谢迈克·沃特金斯:http://mikewatkins.ca/2008 /11/29/python-2-and-3-metaclasses/。如果您希望程序在 Python 2 和 Python 3 中工作,您需要执行以下操作:
我认为作业中的“对象”需要替换为“基类”,但我还没有尝试过(我已经尝试过如图所示的代码)。
Method 3 seems to be very neat, but if you want your program to run in both Python 2 and Python 3, it doesn't work. Even protecting the separate variants with tests for the Python version fails, because the Python 3 version gives a syntax error in Python 2.
Thanks to Mike Watkins: http://mikewatkins.ca/2008/11/29/python-2-and-3-metaclasses/. If you want the program to work in both Python 2 and Python 3, you need to do something like:
I presume that 'object' in the assignment needs to be replaced with the 'BaseClass', but I haven't tried that (I have tried code as illustrated).
我会把我的扔进戒指里。这是一个简单的装饰器。
我认为它相对于其他一些解决方案的优点是:
YourClass
的实现进行任何更改。这包括不需要为您的类使用元类(请注意,上面的元类位于工厂中,而不是“真正的”类)。YourClass
,它看起来像一个类(因为它确实是),并且他们正常使用它。无需使调用者适应工厂函数。YourClass()
实例化的仍然是您实现的YourClass
的真实实例,而不是任何类型的代理,因此不会产生副作用。isinstance(instance, YourClass)
和类似的操作仍然按预期工作(尽管这部分确实需要 abc,因此排除了 Python <2.6)。我确实遇到了一个缺点:真实类的类方法和静态方法不能通过隐藏它的工厂类透明地调用。我很少使用这个,所以我从来没有遇到过这种需要,但是通过在工厂上使用自定义元类来实现 __getattr__() 来委托 all-ish ,可以很容易地纠正它访问真实类的属性。
我实际上发现更有用的一个相关模式(并不是说这些东西经常需要)是“唯一”模式,其中使用相同的参数结果实例化类返回同一个实例。即“每个参数单例”。上面的内容很好地适应了这一点,并且变得更加简洁:
话虽如此,我确实同意一般建议,即如果您认为您需要其中一件东西,您真的应该停下来问问自己是否真的需要。 99%的时间都是亚格尼。
I'll toss mine into the ring. It's a simple decorator.
Benefits I think it has over some of the other solutions:
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).YourClass
, it looks like a class (because it is), and they use it normally. No need to adapt callers to a factory function.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).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. I.e. a "singleton per arguments". The above adapts to this well and becomes even more concise:
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.
也许我误解了单例模式,但我的解决方案是简单实用的(Pythonic?)。此代码实现了两个目标:
Foo
的实例可在任何地方(全局)访问。Foo
只能存在一个实例。这是代码。
输出
Maybe I missunderstand the singleton pattern but my solution is this simple and pragmatic (pythonic?). This code fullfills two goals
Foo
accessiable everywhere (global).Foo
can exist.This is the code.
Output
好吧,除了同意Pythonic关于模块级全局的一般建议之外,这样怎么样:
输出是:
Well, other than agreeing with the general Pythonic suggestion on having module-level global, how about this:
Output is:
怎么样:
将它用作应该是单例的类的装饰器。像这样:
这类似于另一个答案中的
singleton = lambda c: c()
装饰器。与其他解决方案一样,唯一的实例具有类名称 (MySingleton
)。但是,使用此解决方案,您仍然可以通过执行MySingleton()
从类“创建”实例(实际上获取唯一的实例)。它还会阻止您通过执行 type(MySingleton)() 来创建其他实例(也返回相同的实例)。How about this:
Use it as a decorator on a class that should be a singleton. Like this:
This is similar to the
singleton = lambda c: c()
decorator in another answer. Like the other solution, the only instance has name of the class (MySingleton
). However, with this solution you can still "create" instances (actually get the only instance) from the class, by doingMySingleton()
. It also prevents you from creating additional instances by doingtype(MySingleton)()
(that also returns the same instance).这个答案可能不是您想要的。我想要一个单例,因为只有该对象才有其身份,以便进行比较。就我而言,它被用作 Sentinel Value。答案很简单,创建任何对象
mything = object()
并且根据 python 的本质,只有那个东西才会有其身份。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.这将是序列化的问题。如果您尝试从文件(pickle)反序列化对象,它将不会使用 __call__ ,因此它将创建新文件,您可以使用带有 __new__ 的基类继承来防止这种情况。
This will be problem with serialziation. If you try to deserialize object from file (pickle) it will not use
__call__
so it will create new file, you can use base class inheritance with__new__
to prevent that.如果您想使用
实例
作为属性,您可以使用元类
。例如;You can use a
metaclass
if you want to useinstance
as a property. For example;我也更喜欢装饰器语法而不是从元类派生。我的两分钱:
这比提供的其他装饰器有一些好处:
classmethod
和staticmethod
仍将按预期工作__init__()
构造函数仅执行一次缺点:
print(MyClass().__class__.__name__)
将返回Singleton
而不是MyClass
。如果您仍然需要这个,我建议使用上面建议的元类。如果您需要基于构造函数参数的不同实例,则需要改进此解决方案(siddhesh-suhas-sathe<提供的解决方案/a> 提供了这个)。
最后,正如其他人建议的那样,考虑使用 python 中的模块。模块是对象。您甚至可以将它们传递到变量中并将它们注入到其他类中。
I also prefer decorator syntax to deriving from metaclass. My two cents:
This has some benefits above other decorators provided:
isinstance(MyClass(), MyClass)
will still work (returning a function from the clausure instead of a class will make isinstance to fail)property
,classmethod
andstaticmethod
will still work as expected__init__()
constructor is executed only onceCons:
print(MyClass().__class__.__name__)
will returnSingleton
instead ofMyClass
. If you still need this, I recommend using a metaclass as suggested above.If you need a different instance based on constructor parameters this solution needs to be improved (solution provided by siddhesh-suhas-sathe provides this).
Finally, as other suggested, consider using a module in python. Modules are objects. You can even pass them in variables and inject them in other classes.
我只是偶然做了一个简单的,我想我会分享它......
I just made a simple one by accident and thought I'd share it...
与 fab 的答案略有相似,但不完全相同。
单例模式不要求我们能够多次调用构造函数次。由于单例应该被创建一次并且只能创建一次,难道它不应该被视为只被创建一次吗? “欺骗”构造函数可能会损害可读性。
所以我的建议就是这样:
这并不排除用户代码使用构造函数或字段
instance
:...如果您确定
Elvis
有尚未创建,而King
已*。但它鼓励用户普遍使用
the
方法:......特别是(尽管不是唯一)如果对猫王和/或<的预先存在存在疑问那个建筑物。
要完成此操作,您还可以覆盖
__delattr__()
以在尝试删除instance
时引发异常,并覆盖__del__()
这样它就会引发异常(除非我们知道程序正在结束...)* 这里有一些本体论问题:一个单身人士是否可以成为另一个单身人士?对单例类进行子类化似乎充满了问题。即便如此,您可能会问
isinstance()
,而不是is
。进一步改进
我感谢那些提供评论和编辑帮助的人,欢迎提供更多帮助。当我使用 Jython 时,这应该可以更普遍地工作,并且是线程安全的。
注意点:
__new__
__new__
时,你必须装饰与@classmethod或__new__
将是一个未绑定的实例方法the
成为类级别属性,可能重命名它到实例
It is slightly similar to the answer by fab but not exactly the same.
The singleton pattern does not require that we be able to call the constructor multiple times. As a singleton should be created once and once only, shouldn't it be seen to be created just once? "Spoofing" the constructor arguably impairs legibility.
So my suggestion is just this:
This does not rule out the use of the constructor or the field
instance
by user code:... if you know for sure that
Elvis
has not yet been created, and thatKing
has*.But it encourages users to use the
the
method universally:... particularly (though not exclusively) if there is doubt concerning the pre-existence of Elvis and/or of that building.
To make this complete you could also override
__delattr__()
to raise an Exception if an attempt is made to deleteinstance
, and override__del__()
so that it raises an Exception (unless we know the program is ending...)* some ontological issues here: could one singleton ever be another singleton? Subclassing a singleton class seems fraught with problems. And even then you'd probably be asking
isinstance()
, notis
.Further improvements
My thanks to those who have helped with comments and edits, of which more are welcome. While I use Jython, this should work more generally, and be thread-safe.
Points of note:
__new__
__new__
you must decorate with @classmethod or__new__
will be an unbound instance methodthe
a class-level property, possibly renaming it toinstance
我不记得在哪里找到这个解决方案,但从我的非 Python 专家的角度来看,我发现它是最“优雅”的:
为什么我喜欢这个?没有装饰器,没有元类,没有多重继承......如果您决定不再希望它成为单例,只需删除 __new__ 方法即可。由于我是 Python(以及一般的 OOP)新手,我希望有人能告诉我为什么这是一个糟糕的方法?
I can't remember where I found this solution, but I find it to be the most 'elegant' from my non-Python-expert point of view:
Why do I like this? No decorators, no meta classes, no multiple inheritance...and if you decide you don't want it to be a Singleton anymore, just delete the
__new__
method. As I am new to Python (and OOP in general) I expect someone will set me straight about why this is a terrible approach?代码基于 Tolli 的回答。
说明:
创建新类,继承给定的
cls
(它不会修改
cls
,以防有人想要singleton(list)
)创建实例。在覆盖
__new__
之前,这很简单。该函数仅在调用者期望的情况下返回
实例
,否则引发TypeError
。当有人尝试从修饰类继承时,条件不满足。
如果
__new__()
返回cls
的实例,则将调用新实例的__init__()
方法 strong> 类似于 __init__(self[, ...]),其中 self 是新实例,其余参数与传递给 __new__() 的参数相同。< /p>instance
已初始化,因此函数将__init__
替换为不执行任何操作的函数。查看它在线运行
Code based on Tolli's answer.
Explanation:
Create new class, inheriting from given
cls
(it doesn't modify
cls
in case someone wants for examplesingleton(list)
)Create instance. Before overriding
__new__
it's so easy.__new__
using method defined moment ago.The function returns
instance
only when it's what the caller expects, otherwise raisesTypeError
.The condition is not met when someone attempts to inherit from decorated class.
instance
is already initialized, so function replaces__init__
with function doing nothing.See it working online
如果您不需要 Singleton 实例的延迟初始化,那么以下操作应该很简单且线程安全:
这样
A
是在模块导入时初始化的单例。If you don't need lazy initialization of the instance of the Singleton, then the following should be easy and thread-safe:
This way
A
is a singleton initialized at module import.经过一段时间的努力后,我最终想出了以下方法,这样当从单独的模块调用时,配置对象只会加载一次。元类允许将全局类实例存储在内置字典中,目前这似乎是存储适当的程序全局的最简洁的方法。
After struggling with this for some time I eventually came up with the following, so that the config object would only be loaded once, when called up from separate modules. The metaclass allows a global class instance to be stored in the builtins dict, which at present appears to be the neatest way of storing a proper program global.
一条衬垫(我并不自豪,但它确实有效):
One liner (I am not proud, but it does the job):