1._ _init_ _() 和 _ _new_ _()
2.单例模式
1._ _init_ _() 和 _ _new_ _()
①基本区别
_ _new_ _() :创建对象、然后分配内存 -----可参考Java中new 一个对象的概念
_ _init_ _() :初始化对象(并没有创建对象)
②object对象中的new: 创建并返回一个新的对象
@staticmethod # known case of __new__ def __new__(cls, *more): # known special case of object.__new__ """ Create and return a new object. See help(type) for accurate signature. """ pass
可以继承object重写 _ _new_ _
运行顺序是:先执行new(创建对象),再执行init(初始化)
new中:继承父类,MyClass类创建对象instance并返回,返回的对象会传给init中的self,执行初始化。
class MyClass(object): def __init__(self): print("init is running...") # new会先创建对象、然后分配内存 def __new__(cls, *args, **kwargs): pass print("new is running...") instance = super().__new__(cls) # 创建对象(对象是由类创建的,所以是cls) return instance if __name__ == '__main__': a = MyClass() print(type(a)) """ new is running... init is running... <class '__main__.MyClass'> """
2.单例模式
①多例模式,创建多少个对象,就分配多少个内存;每个对象独享一份内存
class MyClass(object): pass if __name__ == '__main__': a = MyClass() b = MyClass() print(id(a)) # 2822273363912 print(id(b)) # 2822273409096
②单例模式
定义:不管创建多少个对象,永远用同一个对象(对象只创建一次)
作用:省内存,提高性能
实现单例模式的方法
1).继承父类实现单例模式
# 怎么实现单例模式?-----在创建对象上做改变 # # 如果对象已经创建 就直接把创建的对象返回 # # 如果对象未创建,创建对象,并把对象返回。 class MyClass(object): __instance = None def __new__(cls, *args, **kwargs): if cls.__instance is None: cls.__instance = super().__new__(cls) return cls.__instance
不管创建多少对象,最终都是同一个对象,只分配一个内存地址。
2)用装饰器实现单例模式
def singleton(cls): isinstance = {} def wrapper(*args, **kwargs): if not isinstance: isinstance[cls] = cls(*args, **kwargs) return isinstance[cls] return wrapper @singleton class MyClass(object): pass if __name__ == '__main__': a = MyClass() b = MyClass() print(id(a)) print(id(b))
3)使用import实现单例模式
deno_2.py
class MyClass(object): pass myclass = MyClass()
用import实现单例模式
from demo_2 import myclass print(id(myclass)) # 2785165023752 from demo_2 import myclass print(id(myclass)) # 2785165023752
不管import几次,只会在第一次分配内存。
(第一次导入模块时,python解释器会创建pyc文件;以后再导入时,不会执行导入代码而是直接使用pyc文件)
pyc不要上传到git上,不同的开发环境可能会出现不兼容的情况。
4)用指定的类方法实现单例模式
# 实现单例 与 非单例共存 class MyClass(object): __instance = None def __init__(self, *args, **kwargs): pass @classmethod def singleton(cls ,*args, **kwargs): if not cls.__instance: cls.__instance = cls(*args, **kwargs) return cls.__instance if __name__ == '__main__': a = MyClass.singleton() b = MyClass.singleton() c = MyClass() d = MyClass() print(id(a)) # 2514584144968 print(id(b)) # 2514584144968 print(id(c)) # 2514584145032 print(id(d)) # 2514584145096
python对象创建的内存什么时候释放?
python的垃圾回收机制。
举例,上面的类创建的对象放在一个main()函数中,调用main函数并执行完毕后,函数内部的对象会全部销毁。
# 实现单例 与 非单例共存 class MyClass(object): __instance = None def __init__(self, *args, **kwargs): pass @classmethod def singleton(cls ,*args, **kwargs): if not cls.__instance: cls.__instance = cls(*args, **kwargs) return cls.__instance def main(): a = MyClass.singleton() b = MyClass.singleton() c = MyClass() d = MyClass() print(id(a)) # 1577432513672 print(id(b)) # 1577432513672 print(id(c)) # 1577432513736 print(id(d)) # 1577432513800
if __name__ == '__main__':
main()
所以,要尽可能少的定义全局变量,如果不写del 变局变量,全局变量是不会被销毁的,除非程序结束。
5)创建线程安全的单例模式
什么是线程不安全?看下面的例子:虽然使用了单例模式,但是在多线程的情况下,单例模式“失效”了
原因:多线程的情况下,会同时执行下面黄色背景的代码段,就会造成创建多个不同的对象的情况。
# 线程不安全 # 多并发时,多线程造成的非单例 import threading import time class MyClass(object): __instance = None def __init__(self, *args, **kwargs): pass @classmethod def singleton(cls ,*args, **kwargs): if not cls.__instance: cls.__instance = cls(*args, **kwargs) return cls.__instance def my_obj(): # 打印 查看创建内存 print(MyClass.singleton()) return MyClass.singleton() if __name__ == '__main__': # 创建10个线程 tasks = [threading.Thread(target=my_obj) for i in range(10)] # 同时启动10个线程 for task in tasks: task.start() """
<__main__.MyClass object at 0x000002991B431608>
<__main__.MyClass object at 0x000002991B431708>
<__main__.MyClass object at 0x000002991B431588>
<__main__.MyClass object at 0x000002991B431508>
<__main__.MyClass object at 0x000002991B4316C8>
<__main__.MyClass object at 0x000002991B431748>
<__main__.MyClass object at 0x000002991B431688>
<__main__.MyClass object at 0x000002991B4315C8>
<__main__.MyClass object at 0x000002991B431548>
"""
如何创建线程安全的单例模式呢?-----创建锁,加锁
原理:将创建对象条件的代码段加锁,锁住后,同时只能有一个线程在创建对象,创建对象结束后,会自动释放锁。
第二次再创建的时候,instance非none,就实现了单例模式了。
import threading import time class MyClass(object): __instance = None # 创建锁 __lock = threading.Lock() def __init__(self, *args, **kwargs): pass @classmethod def singleton(cls ,*args, **kwargs): with cls.__lock: # with 语句段 结束后会自动释放锁(unlock) if not cls.__instance: cls.__instance = cls(*args, **kwargs) return cls.__instance def my_obj(): # 打印 查看创建内存 print(MyClass.singleton()) return MyClass.singleton() if __name__ == '__main__': # 创建10个线程 tasks = [threading.Thread(target=my_obj) for i in range(10)] # 同时启动10个线程 for task in tasks: task.start() """ <__main__.MyClass object at 0x000001F931AC1448> <__main__.MyClass object at 0x000001F931AC1448> <__main__.MyClass object at 0x000001F931AC1448> <__main__.MyClass object at 0x000001F931AC1448> <__main__.MyClass object at 0x000001F931AC1448> <__main__.MyClass object at 0x000001F931AC1448> <__main__.MyClass object at 0x000001F931AC1448> <__main__.MyClass object at 0x000001F931AC1448> <__main__.MyClass object at 0x000001F931AC1448> <__main__.MyClass object at 0x000001F931AC1448> """
__call__魔术方法:
# 下面的匿名函数是含有__call__属性的,所以是callable的;反之没有__call__属性,就是不可调用的 print(dir(lambda x, y: x+y)) # ['__annotations__', '__call__'.....] print(callable(lambda x,y:x+y)) # True print((lambda x,y:x+y)(1,2)) # 3 class MyClass(object): pass if __name__ == '__main__': a = MyClass() print(a()) # TypeError: 'MyClass' object is not callable
只要它含有__call__属性,这个对象就是callable的,对象后就可以加()进行调用
举例说明