• 6 单列模式 _ _new_ _


    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的,对象后就可以加()进行调用

    举例说明

  • 相关阅读:
    vim内外部鼠标复制 粘贴
    nginx 问题解决nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
    Installation of the latest version of netease-cloud-music on Fedora 30 linux platform
    Linux就该这么学11学习笔记
    Linux就该这么学10学习笔记
    css day2
    Linux就该这么学09学习笔记
    Linux就该这么学08学习笔记
    css day1
    Linux就该这么学07学习笔记
  • 原文地址:https://www.cnblogs.com/ananmy/p/14083647.html
Copyright © 2020-2023  润新知