Python实现单例模式的5种方法详解与对比
本文详细解析Python中实现单例模式的五种常见方式,包括classmethod装饰器、类装饰器、元类、模块导入以及重写__new__()方法。通过代码示例对比各方法的运行结果,重点指出前四种方法在特定条件下可能创建新对象,并非绝对单例,而重写__new__()方法能确保全局仅创建一次实例,适用于需要严格单例控制的场景。
classmethod装饰器实现单例
第一种方法利用classmethod装饰器来实现单例。
# 全局变量
ip = '192.168.13.98'
port = '3306'
class MySQL:
__instance = None
def __init__(self, ip, port):
self.ip = ip
self.port = port
@classmethod
def instance(cls, *args, **kwargs):
if args or kwargs:
cls.__instance = cls(*args, **kwargs)
return cls.__instance
obj1 = MySQL.instance(ip, port)
obj2 = MySQL.instance()
obj3 = MySQL.instance()
print(obj1)
print(obj2, obj2.__dict__)
print(obj3, obj3.__dict__)
执行上述代码后的输出结果如下。
<main.MySQL object at 0x058D6F30> <main.MySQL object at 0x058D6F30> {'ip': '192.168.13.98', 'port': '3306'} <main.MySQL object at 0x058D6F30> {'ip': '192.168.13.98', 'port': '3306'}
类装饰器实现单例
第二种方法通过自定义类的装饰器来实现单例逻辑。
def singlegon(cls):
_instance = cls(ip, port)
def wrapper(*args, **kwargs):
if args or kwargs:
return cls(*args, **kwargs)
return _instance
return wrapper
@singlegon
class MySQL1:
def __init__(self, ip, port):
self.ip = ip
self.port = port
obj1 = MySQL1()
obj2 = MySQL1()
obj3 = MySQL1('1.1.1.3', 8080)
print(obj1)
print(obj2, obj2.__dict__)
print(obj3, obj3.__dict__)
该方式运行后的具体输出结果如下。
<main.MySQL1 object at 0x04C102B0> <main.MySQL1 object at 0x04C102B0> {'ip': '192.168.13.98', 'port': '3306'} <main.MySQL1 object at 0x04C10310> {'ip': '1.1.1.3', 'port': 8080}
元类实现单例
第三种方法借助元类(metaclass)机制来保证单例。
class Mymetaclass(type):
def __init__(self, class_name, class_bases, class_dic):
super().__init__(class_name, class_bases, class_dic)
self.__instance = self(ip, port)
def __call__(self, *args, **kwargs):
if args or kwargs:
obj = self.__new__(self)
self.__init__(obj, *args, **kwargs)
self.__instance = obj
return self.__instance
class MySQL2(metaclass=Mymetaclass):
def __init__(self, ip, port):
self.ip = ip
self.port = port
obj1 = MySQL2()
obj2 = MySQL2()
obj3 = MySQL2('1.1.1.3', 80)
print(obj1)
print(obj2, obj2.__dict__)
print(obj3, obj3.__dict__)
使用元类实现时的运行结果如下所示。
<main.MySQL2 object at 0x04D003B0> <main.MySQL2 object at 0x04D003B0> {'ip': '192.168.13.98', 'port': '3306'} <main.MySQL2 object at 0x04D003D0> {'ip': '1.1.1.3', 'port': 80}
模块导入实现单例
第四种方法利用Python模块导入的特性来实现单例,因为模块在Python中只会被加载一次。
# instance.py
class MySQL:
def __init__(self, ip, port):
self.ip = ip
self.port = port
ip = '192.168.13.98'
port = 3306
instance = MySQL(ip, port)
# 测试代码
import os, sys
sys.path.append(os.path.dirname(os.path.dirname(__file__)))
from test import instance
obj1 = instance.instance
obj2 = instance.instance
obj3 = instance.MySQL('1.1.1.3', 80)
print(obj1)
print(obj2, obj2.__dict__)
print(obj3, obj3.__dict__)
通过模块导入方式实现的运行结果如下。
<day30.instance.MySQL object at 0x052B0AB0> <day30.instance.MySQL object at 0x052B0AB0> {'ip': '192.168.13.98', 'port': 3306} <day30.instance.MySQL object at 0x052B03F0> {'ip': '1.1.1.3', 'port': 80}
重写__new__()实现绝对单例
第五种方法通过重写类的__new__()魔术方法来实现单例。
class MySQL3(object):
__instance = None
__first_init = True
def __init__(self, ip, port):
if self.__first_init:
self.ip = ip
self.port = port
self.__first_init = False
def __new__(cls, *args, **kwargs):
if not cls.__instance:
cls.__instance = object.__new__(cls)
return cls.__instance
obj1 = MySQL3(ip, port)
obj2 = MySQL3(ip, port)
obj3 = MySQL3('1.1.1.3', 80)
print(obj1)
print(obj2, obj2.__dict__)
print(obj3, obj3.__dict__)
重写__new__()方法后的运行结果如下。
<main.MySQL3 object at 0x059603F0> <main.MySQL3 object at 0x059603F0> {'ip': '192.168.13.98', 'port': '3306', '_MySQL3__first_init': False} <main.MySQL3 object at 0x059603F0> {'ip': '192.168.13.98', 'port': '3306', '_MySQL3__first_init': False}
需要注意的是,前四种方法虽然被称为实现了单例模式,但并非绝对单例。在上述示例中,如果调用时传入了参数,它们可能会创建新的对象实例。相比之下,第五种重写__new__()的方法属于绝对单例模式,能够确保全局范围内真正只创建一次对象实例。
以上内容详细阐述了Python实现单例模式的五种方法。如需了解更多关于Python单例模式的信息,建议参考本站其他相关文章。
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













