Python实现单例模式
单例模式
介绍 意图:保证一个类仅有一个实例,并提供一个访问它的全局访问点。 主要解决:一个全局使用的类频繁地创建与销毁。 何时使用:当您想控制实例数目,节省系统资源的时候。 如何解决:判断系统是否已经有这个单例,如果有则返回,如果没有则创建。 关键代码:构造函数是私有的。 应用实例: 1、一个班级只有一个班主任。 2、Windows 是多进程多线程的,在操作一个文件的时候,就不可避免地出现多个进程或线程同时操作一个文件的现象,所以所有文件的处理必须通过唯一的实例来进行。 3、一些设备管理器常常设计为单例模式,比如一个电脑有两台打印机,在输出的时候就要处理不能两台打印机打印同一个文件。 优点: 1、在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例(比如管理学院首页页面缓存)。 2、避免对资源的多重占用(比如写文件操作)。 缺点:没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。 使用场景: 1、要求生产唯一序列号。 2、WEB 中的计数器,不用每次刷新都在数据库里加一次,用单例先缓存起来。 3、创建的一个对象需要消耗的资源过多,比如 I/O 与数据库的连接等。
第一种方法
1 class User(object): 2 #定义类属性 3 __instance=None 4 #初始化方法 5 def __init__(self,name): 6 self.name=name 7 8 @classmethod 9 #定义类方法 10 def get_instance(cls,name): 11 #如果__instance为空,则执行 12 if not cls.__instance: 13 #实例化对象,并赋值给__instance属性 14 cls.__instance=User(name) 15 return cls.__instance 16 u1 =User.get_instance("lili") 17 u2 = User.get_instance("lisi") 18 print(u1==u2) 19 print(id(u1)) 20 print(id(u2))
运行结果:
True 41816192 41816192
代码分析
# 要实现不管实例化多少次,拿到的都是同一个对象 # 这里我们通过调用类的静态方法来实例化类(@classmethod) # 第一次实例化对象的时候,类属性为空,则会初始化对象,并返回对象 # 第二次时,类属性已不为空,则不会实例化对象,直接返回第一次生成的对象
关键点:通过类静态方法实例化对象
__instance类属性的作用
补充classmethod
classmethod 修饰符对应的函数不需要实例化,不需要 self 参数,但第一个参数需要是表示自身类的 cls 参数,可以来调用类的属性,类的方法,实例化对象等。
这里name是否相同?
# 我们是使用通过调用类的静态方法来实例化对象的,所以这里不会直接执行__init__方法,是在静态方法里间实例化时执行了__init__方法,并且只会执行一次。
# 这里每次实例化一次对象,拿到的对象是同一个对象,name也相同。
有一个问题?我们知道有两种方法初始化对象,如果我们使用User("lili")来实例化对象会怎样?
# 第一种:u1=User("lili") #通过 __init__实例化对象 # 第二种:u1=get_instance("lili") #通过类的静态方法间接实例化对象
class User(object): #定义类属性 __instance=None #初始化方法 def __init__(self,name): self.name=name @classmethod #定义类方法 def get_instance(cls,name): #如果__instance为空,则执行 if not cls.__instance: #实例化对象,并赋值给__instance属性 cls.__instance=User(name) return cls.__instance u1 =User("lili") u2 = User("lisi") print(u1==u2) print(id(u1)) print(id(u2))
False 36376704 36376760
第二种方法
1 class User(object): 2 #定义类属性 3 __instance=None 4 #初始化方法 5 def __init__(self,name): 6 self.name=name 7 def __new__(cls,name): 8 #保证object.__new__(cls)方法只会调用第一次 9 if not cls.__instance: 10 #object.__new__(cls)创建对象 11 cls.__instance=object.__new__(cls) 12 return cls.__instance 13 u1 = User("lili") 14 u2 = User("lisi") 15 print(u1==u2) 16 print(id(u1)) 17 print(id(u2))
运行结果:
True 42106384 42106384
代码分析
#当代码执行u1 = User("lili") # 会运行到__new__方法,此时类属性__instance为空,则会创建对象,把对象赋值给__instance,并返回对象 # 现在代码还没有运行完,会继续执行__init__方法,会将"lili"赋值给name,现在name="lili" # 执行u1 = User("lili"),类属性不为空,不会创建对象,直接返回第一次创建的对象 #此时是最重要的,def __init__(self,name)中self为第一次的对象,而传进来的name为第二次的name,就会将"lisi"赋值给name,现在name="lisi" # 但是对象永远都是那个对象,name却不是那个name了。
#所以这里每次实例化一次对象,虽然对象还是那个对象,但是name会不一样。