在 Python 中实现单例的最佳方式是什么?
Python中实现单例的最佳方式
尽管单例设计模式的优劣不是本文的讨论重点,本文将探讨如何在 Python 中以最符合 Python 风格的方式实现这一模式。在这里,"最符合 Python 风格" 的意思是遵循"最少惊讶原则"。
实现方法
方法 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 本身是一个函数,而不是一个类,因此无法调用类方法。
方法 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。
优点:
- 简单胜于复杂。
缺点:
- 未延迟实例化。
推荐方法
我建议使用 方法 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__,请将以下代码添加到 Singleton.__call__ 中的 if 语句中:
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
元类的作用
元类是类的类,也就是说,类是其元类的实例。可以通过 type(obj) 找到 Python 中对象的元类。正常的新的类是 type 类型。上面的 Logger 将属于 class 'your_module.Singleton' 类型,就像 Logger 的(唯一)实例将属于 class 'your_module.Logger' 类型一样。当使用 Logger() 调用 logger 时,Python 首先询问 Logger 的元类 Singleton 应该做什么,允许抢先进行实例创建。这个过程与 Python 通过调用 __getattr__ 询问类应该做什么来处理其属性类似,而你通过执行 myclass.attribute 引用其属性。
元类本质上决定了调用类的含义以及如何实现该含义。请参阅例如 http://code.activestate.com/recipes/498149/,它使用元类主要在 Python 中重新创建 C 样式结构。讨论线程 [元类的具体用例有哪些?](https://codereview.stackexchange.com/questions/82786/what-are-some-concrete-use-cases-for-metaclasses) 也提供了一些示例,它们通常与声明性编程有关,尤其是在 ORM 中使用。
在这种情况下,如果你使用你的 方法 2,并且一个子类定义了一个 __new__ 方法,它将在每次调用 SubClassOfSingleton() 时执行,因为它负责调用返回存储的实例的方法。使用元类,它只会执行一次,即在创建唯一实例时。你需要自定义调用类的定义,这是由其类型决定的。
一般来说,使用元类来实现单例是有意义的。单例很特别,因为它的实例只创建一次,而元类是自定义创建类的实现方式,使其行为与普通类不同。使用元类可以让你在其他方式需要自定义单例类定义时有更多的控制权。
当然
你的单例不需要多重继承(因为元类不是基类),但对于继承创建类的子类,你需要确保单例类是第一个/最左边的元类,重新定义 __call__。这不太可能有问题。实例字典并不在实例的名称空间中,因此不会意外地覆盖它。
你还会听到单例模式违反了"单一职责原则",即每个类都应该只做一件事。这样,你不必担心在需要更改另一种代码时破坏代码所做的某一件事,因为它们是独立且封装的。元类实现通过了此测试。元类负责强制模式,创建的类和子类不需要意识到它们是单例。方法 1 没有通过此测试,正如你用"MyClass 本身是一个函数,而不是类,因此无法调用类方法"指出的那样。
Python 2 和 3 兼容版本
在 Python2 和 3 中编写代码需要使用稍微复杂一点的方案。由于元类通常是 type 类的子类,因此可以使用一个元类在运行时动态创建一个带有它作为元类的中介基类,然后使用该基类作为公共单例基类的基类。这说起来比做起来难,如下所示:
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
这种方法的一个讽刺之处在于它使用子类化来实现元类。一个可能的优点是,与纯元类不同的是,isinstance(inst, Singleton) 将返回 True。
修正
关于另一个话题,你可能已经注意到了,但你原始帖子中的基类实现是错误的。需要在类中引用 _instances,你需要使用 super(),或者是类方法的静态方法,因为在调用时实际类尚未创建。所有这些对于元类实现也是适用的。
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
以上是在 Python 中实现单例的最佳方式是什么?的详细内容。更多信息请关注PHP中文网其他相关文章!

热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

Video Face Swap
使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

热工具

记事本++7.3.1
好用且免费的代码编辑器

SublimeText3汉化版
中文版,非常好用

禅工作室 13.0.1
功能强大的PHP集成开发环境

Dreamweaver CS6
视觉化网页开发工具

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

Python适合数据科学、Web开发和自动化任务,而C 适用于系统编程、游戏开发和嵌入式系统。 Python以简洁和强大的生态系统着称,C 则以高性能和底层控制能力闻名。

Python在游戏和GUI开发中表现出色。1)游戏开发使用Pygame,提供绘图、音频等功能,适合创建2D游戏。2)GUI开发可选择Tkinter或PyQt,Tkinter简单易用,PyQt功能丰富,适合专业开发。

2小时内可以学会Python的基本编程概念和技能。1.学习变量和数据类型,2.掌握控制流(条件语句和循环),3.理解函数的定义和使用,4.通过简单示例和代码片段快速上手Python编程。

Python更易学且易用,C 则更强大但复杂。1.Python语法简洁,适合初学者,动态类型和自动内存管理使其易用,但可能导致运行时错误。2.C 提供低级控制和高级特性,适合高性能应用,但学习门槛高,需手动管理内存和类型安全。

两小时内可以学到Python的基础知识。1.学习变量和数据类型,2.掌握控制结构如if语句和循环,3.了解函数的定义和使用。这些将帮助你开始编写简单的Python程序。

要在有限的时间内最大化学习Python的效率,可以使用Python的datetime、time和schedule模块。1.datetime模块用于记录和规划学习时间。2.time模块帮助设置学习和休息时间。3.schedule模块自动化安排每周学习任务。

Python在自动化、脚本编写和任务管理中表现出色。1)自动化:通过标准库如os、shutil实现文件备份。2)脚本编写:使用psutil库监控系统资源。3)任务管理:利用schedule库调度任务。Python的易用性和丰富库支持使其在这些领域中成为首选工具。

Python在web开发、数据科学、机器学习、自动化和脚本编写等领域有广泛应用。1)在web开发中,Django和Flask框架简化了开发过程。2)数据科学和机器学习领域,NumPy、Pandas、Scikit-learn和TensorFlow库提供了强大支持。3)自动化和脚本编写方面,Python适用于自动化测试和系统管理等任务。
