用python实现单例模式_Python 实现单例模式的几种常见方式

单例模式(Singleton Pattern)是一种常用的开发设计模式,它的主要目的就是确保只有一个实例对象的存在。换句话说,当一个类的功能比较单一,只需要一个实例对象就可以完成需求的时,就可以使用单例模式来节省内存资源。

1. 模块实现单例

python模块在第一导入时,会生成.pyc编译文件,当再次导入时,就直接加载.pyc编译文件,而不会再次执行模块代码,所以可以说模块就是一个天然的单例模式。利用模块这个特性,只需把相关函数和数据定义在一个模块中,将它导入到其他模块使用就可以达到单例模式的效果。

# person.py

class Person(object):

def __init__(self,name, age):

self.name = name

self.age = age

person_instance = Person('张三',18)

# test.py

from person import person_instance

if __name__ == '__main__':

p1 = person_instance

p2 = person_instance

p2.name = '李四'

p2.age = 22

print(p1)

print(p2)

print(p1 == p2)

# 输出结果

True

2. 装饰器实现单例

装饰器的作用是给函数或者类扩展功能,所以可以利用装饰器来让类只生成一个实例对象。在装饰器里定义一个字典,用来存放类的实例。当第一次创建时,将这个实例保存到这个字典中。然后以后每次创建对象的时候,都去这个字典中查找是否已经存在。如果存在,就直接返回这个实例对象,否则,就创建实例并保存到字典中。使用函数的方式创建装饰器

# 示例代码

def singleton(cls):

_instance = {}

def decorated(*args, **kwargs):

if cls not in _instance:

_instance[cls] = cls(*args, **kwargs)

return _instance[cls]

return decorated

@singleton

class Person(object):

def __init__(self, name, age):

self.name = name

self.age = age

if __name__ == '__main__':

p1 = Person('张三',18)

p2 = Person('李四',22)

print(p1)

print(p2)

print(p1==p2)

# 输出结果

True使用类的方式创建装饰器

# 示例代码

class Singleton(object):

def __init__(self,cls):

self._cls = cls

self._instance = {}

def __call__(self, *args):

if self._cls not in self._instance:

self._instance[self._cls] = self._cls(*args)

return self._instance[self._cls]

@Singleton

class Person(object):

def __init__(self, name, age):

self.name = name

self.age = age

if __name__ == '__main__':

p1 = Person('张三',18)

p2 = Person('李四',22)

print(p1)

print(p2)

print(p1==p2)

# 输出结果

True

3. 使用类方法实现单例

当使用类直接创建实例对象的时候,创建的并不是单例对象,那么可以在类中定义一个类方法来实现单例模式,主要思路就是会去判断类是否有_instance这个属性,如果有则直接返回这个实例,没有则创建实例。

# 示例代码

class Person(object):

def __init__(self,name, age):

self.name = name

self.age = age

@classmethod

def get_instance(cls, *args, **kwargs):

if not hasattr(cls, '_instance'):

cls._instance = cls(*args, **kwargs)

return cls._instance

if __name__ == '__main__':

p1 = Person.get_instance('张三',18)

p2 = Person.get_instance('李四',22)

print(p1)

print(p2)

print(p1 == p2)

# 输出结果

True

4. 使用__new__方法实现单例

Python中一个对象实例化过程大致为:首先执行类的__new__方法,如果没有,默认会调用父类的__new__方法,返回一个实例化对象。然后在调用__init__方法对此对象进行初始化。我们可以利用这个过程,实现单例模式。在类的__new__方法中,判断是否存在实例,如果存在,直接返回实例,否则,创建一个实例。这样就能够达到单例模式效果了。

# 示例代码

class Person(object):

def __new__(cls, *args, **kwargs):

if not hasattr(cls, '_instance'):

cls._instance = super().__new__(cls)

return cls._instance

def __init__(self,name, age):

self.name = name

self.age = age

if __name__ == '__main__':

p1 = Person('张三',18)

p2 = Person('李四',22)

print(p1)

print(p2)

print(p1 == p2)

# 输出结果

True

5. 使用元类metaclass实现单例

Python中元类是控制类创建的类,所以可以在创建类的时候通过元类来创建类,在元类的__call__方法中,判断是否存在实例,如果存在,直接返回实例,否则,创建一个实例,使其只存在一个实例对象。

# 示例代码

class Singleton(type):

def __call__(cls, *args, **kwargs):

if not hasattr(cls, "_instance"):

cls._instance = super().__call__(*args, **kwargs)

return cls._instance

class Person(metaclass=Singleton):

def __init__(self,name, age):

self.name = name

self.age = age

if __name__ == '__main__':

p1 = Person('张三',18)

p2 = Person('李四',22)

print(p1)

print(p2)

print(p1 == p2)

# 输出结果

True

6. 存在的问题

在使用类方法、__new__方法、元类来实现单例时,在单线程下是安全的。但是如果遇到多个线程同时创建这个类的实例的时候就会出现问题。多线程下存在同时创建多个实例的现象,属于线程安全问题

当加入多线程去创建实例对象的时候,如果执行速度够快还不会出现影响,当执行速度不够快的时候,一个线程去创建实例然后拿到了_instance这个属性去判断,其他的线程也可能会拿到_instance这个属性,发现并没有实例存在。此时,这两个线程就会同时创建一个实例,造成同时创建多个实例的现象,那么单例模式自然也就失效了。

# 示例代码

import threading

import time

class Person(object):

def __init__(self,name, age):

self.name = name

self.age = age

time.sleep(1)

@classmethod

def get_instance(cls, *args, **kwargs):

if not hasattr(cls, '_instance'):

cls._instance = cls(*args, **kwargs)

return cls._instance

def task(arg):

p = Person.get_instance('张三',18)

print(p)

if __name__ == '__main__':

for i in range(10):

t = threading.Thread(target=task, args=[i,])

t.start()

# 输出结果

<__main__.person object at>解决办法是加线程锁

在获取对象属性_instance的时候加锁,让每一个线程使用完后才释放它,接着下一个线程继续运行程序,就可以解决同时获取对象并创建不同的实例的问题。

# 示例代码-类方法实现单例(改进版)

import threading

import time

class Person(object):

_lock = threading.Lock()

def __init__(self,name, age):

self.name = name

self.age = age

time.sleep(1)

@classmethod

def get_instance(cls, *args, **kwargs):

with cls._lock:

if not hasattr(cls, '_instance'):

cls._instance = cls(*args, **kwargs)

return cls._instance

def task(arg):

p = Person.get_instance('张三',18)

print(p)

if __name__ == '__main__':

for i in range(10):

t = threading.Thread(target=task, args=[i,])

t.start()

# 输出结果

# 示例代码__new__方法实现单例(改进版)

# 示例代码-__new__方法实现单例(改进版)

import threading

import time

class Person(object):

_lock = threading.Lock()

def __new__(cls, *args, **kwargs):

time.sleep(1)

with cls._lock:

if not hasattr(cls, '_instance'):

cls._instance = super().__new__(cls)

return cls._instance

def __init__(self,name, age):

self.name = name

self.age = age

def task(arg):

p = Person('张三',18)

print(p)

if __name__ == '__main__':

for i in range(10):

t = threading.Thread(target=task, args=[i,])

t.start()

# 示例代码-元类实现单例(改进版)

# 示例代码-元类实现单例(改进版)

import threading

import time

class Singleton(type):

_lock = threading.Lock()

def __call__(cls, *args, **kwargs):

time.sleep(1)

with cls._lock:

if not hasattr(cls, "_instance"):

cls._instance = super().__call__(*args, **kwargs)

return cls._instance

class Person(metaclass=Singleton):

def __init__(self,name, age):

self.name = name

self.age = age

def task(arg):

p = Person('张三',18)

print(p)

if __name__ == '__main__':

for i in range(10):

t = threading.Thread(target=task, args=[i,])

t.start()

以上是整理的Python中实现单例模式的几种方式,希望对大家学习Python有所帮助。

持续创作需要您的支持,拜托了~~~

如果能帮我关注公众号【不灵兔】那就再好不过了知乎视频​www.zhihu.comzhihu-card-default.svg

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值