V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
wuwukai007
V2EX  ›  Python

网上流传最广的 Python 单例模式竟然是有问题的?

  •  
  •   wuwukai007 · 2022-06-23 17:35:46 +08:00 · 5134 次点击
    这是一个创建于 869 天前的主题,其中的信息可能已经有所发展或是发生改变。
    class AppConfig:
        def __new__(cls, *args, **kwargs):
            if not hasattr(cls,'_instance'):
                cls._instance=super(AppConfig,cls).__new__(cls)
            return cls._instance
    
    app = AppConfig()
    app.name = '单例模式'
    del app
    app2 = AppConfig()
    print("app2 name=",app2.name)
    
    • 输出 app2 name= 单例模式
      虽然是单例,但是我可以删除对象的,删除后重新创建对象,还是老的对象
    第 1 条附言  ·  2022-06-23 18:14:41 +08:00
    from weakref import WeakValueDictionary
    class AppConfig(object):
    
        _instance = WeakValueDictionary()
        def __new__(cls):
            if cls not in cls._instance:
                inst = super().__new__(cls)
                cls._instance[cls] = inst
            return cls._instance[cls]
    
    app = AppConfig()
    app.name = 2
    del app
    app2 = AppConfig()
    print("app2 name=",app2.name,)
    
    • 输出: AttributeErr or: 'AppConfig' object has no attribute 'name'
    • 使用弱引用可以解决
    26 条回复    2022-07-09 20:07:31 +08:00
    leavic
        1
    leavic  
       2022-06-23 17:43:32 +08:00
    我今天才知道 python 还有 del app 这种写法
    dcsuibian
        2
    dcsuibian  
       2022-06-23 17:47:04 +08:00 via Android
    作用域不同。
    dcsuibian
        3
    dcsuibian  
       2022-06-23 17:53:38 +08:00 via Android
    似乎说法不太对。app 和 AppConfig 类的_instance 都引用一个东西,app 这个引用消失了,但引用的东西没消失
    Hstar
        4
    Hstar  
       2022-06-23 17:53:54 +08:00   ❤️ 1
    因为 del app 并没有删除对象,只是删掉了这个对象在此的引用
    cz5424
        5
    cz5424  
       2022-06-23 17:55:14 +08:00
    del app 并且 del app. _instance 试试
    wuwukai007
        6
    wuwukai007  
    OP
       2022-06-23 17:59:55 +08:00
    @cz5424 这个代码一致性就破坏了
    ChrisFreeMan
        7
    ChrisFreeMan  
       2022-06-23 18:05:46 +08:00 via iPhone
    你要手动实现__del__
    wuwukai007
        8
    wuwukai007  
    OP
       2022-06-23 18:06:32 +08:00
    @ChrisFreeMan 试过了,不行的,你试试看?__del__ 只有在程序结束后才会调用,del 不会调用的
    nyxsonsleep
        9
    nyxsonsleep  
       2022-06-23 18:13:29 +08:00
    这个问题如果用过 pyqt 应该会映像深刻。。。
    个人理解 del 实质上没有删除对象的存在,有时会导致 pyqt 删除 gui 时出问题。
    deplivesb
        10
    deplivesb  
       2022-06-23 18:13:54 +08:00
    python 里面的 del 可和 c 系列的 del 不是一个作用,del 也不会调用 __del__
    fatigue
        11
    fatigue  
       2022-06-23 18:14:24 +08:00
    _instance 是挂在类下面的, 你 AppConfig()的时候把这个引用返回给你,你 del 的时候把这个引用删除,_instance 一直都在,下次 AppConfig()再返回给你
    ChrisFreeMan
        12
    ChrisFreeMan  
       2022-06-23 18:27:20 +08:00
    __del__试了下确实不行,这样,其实你是把单例对象赋值给了类,其实那个单例对象是个类对象
    你用反射检查一下有没有单例对象再删除。
    class Test:
    def __new__(cls, *args, **kwargs):
    if not hasattr(cls, '_instance'):
    cls._instance = super().__new__(cls)
    return cls._instance


    app = Test()
    app.name = 'some thing1'
    if hasattr(Test, '_instance'):
    del Test._instance
    app2 = Test()
    print(app2.name)
    ysy950803
        13
    ysy950803  
       2022-06-23 18:41:41 +08:00
    依附于类,而不是对象。
    jinliming2
        14
    jinliming2  
       2022-06-23 21:49:39 +08:00   ❤️ 1
    单例模式允许删除重新创建吗?
    我理解的单例模式,一个类只能有一个对象,其他人来访问的时候可以直接拿到这个对象。
    但没听说过还能有人可以删除掉这个单例对象,然后让重新创建一个……?
    ospider
        15
    ospider  
       2022-06-23 22:15:25 +08:00
    除了面试,没用过单例模式……
    ruanimal
        16
    ruanimal  
       2022-06-23 22:55:37 +08:00
    lz 没理解 del 和 Python 对象,4 楼的是正解
    zxCoder
        17
    zxCoder  
       2022-06-23 23:05:41 +08:00
    @jinliming2 我也没听说过,之前学过的都是你这种说法
    xuboying
        18
    xuboying  
       2022-06-23 23:49:28 +08:00
    好像 del 的行为只是一个请求,实际删除 instance 的时机是不可控的。即使用了 weakref ,在官方例子中还调用了 gc 回收,才能强制请求删除。
    还有一个问题是不确定这个行为在非 cPython 的环境下是不是一致的。
    lanlanye
        19
    lanlanye  
       2022-06-24 08:18:58 +08:00 via iPhone
    Python 最流行的单例模式难道不是直接初始化然后 import ?
    InvincibleDream
        20
    InvincibleDream  
       2022-06-24 09:14:52 +08:00
    dicc
        21
    dicc  
       2022-06-24 16:13:33 +08:00
    你主动删除的,不关单例的事吧..
    catsoul
        22
    catsoul  
       2022-06-24 16:54:56 +08:00
    单例模式正常来说是不让你销毁这个唯一实例的...
    Macv1994
        23
    Macv1994  
       2022-06-25 14:03:17 +08:00
    单例模式不就是为了只有一个实例么?
    qbqbqbqb
        24
    qbqbqbqb  
       2022-07-04 15:46:59 +08:00
    del 不是删除对象,是删除变量。可以近似理解成,语义上相当于一个更强的"=null"(和 Java ,C#等语言相比).
    就和你在 Java 里写如下代码一样:
    AppConfig app = AppConfig.getInstance();
    app = null;
    这样做显然不会销毁单例。和 Python 里的这种情况是一回事。
    yagamil
        25
    yagamil  
       2022-07-09 17:33:00 +08:00
    其实楼主给出的解决方案也挺好的呀。
    Kobayashi
        26
    Kobayashi  
       2022-07-09 20:07:31 +08:00
    你咋不说它实现的不是线程安全的,多线程情况下可能同时创建多个实例呢?:P
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2885 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 02:38 · PVG 10:38 · LAX 18:38 · JFK 21:38
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.