在模块之间共享单例

11

考虑两个模块main和x,具体内容如下:

main:

class Singleton(object):
    _instance = None
    def __new__(cls, *args, **kwargs):
        if not cls._instance:
            cls._instance = super(Singleton, cls).__new__(cls, *args, **kwargs)
        cls._instance.x = 10
        return cls._instance
uvw = Singleton()

if __name__ == "__main__":
    print(id(uvw))
    uvw.x += 10
    print(uvw.x)
    import x

分别为 x 和 y:

import main

print(id(main.uvw))
print(main.uvw.x)
我现在预期执行主函数会在两个实例中产生相同的ID和值为20,但我得到的是这样的结果:
$ python main.py
140592861777168
20
140592861207504
10

有没有办法确保uvw在两个地方是同一个对象?


请注意,您的__new__方法的整个目的是为了防止您需要访问main.uvw,因为Singleton()每次都保证返回相同的实例。 - Eric
1
所以 main 调用了 x,而 x 又在调用 main...?这是一种混乱/灾难的做法。 - Andy Hayden
可能是导入模块:__main__ vs import as module的重复问题。 - Martijn Pieters
你同时使用了__main__x模块,它们是不同的命名空间。请查看重复项。 - Martijn Pieters
3个回答

8
我认为问题在于你的Singleton类被重新加载了,因此在第二个模块中失去了_instance字段。
我认为这样做会起作用: singleton.py:
class Singleton(object):
    _instance = None
    def __new__(cls, *args, **kwargs):
        if not cls._instance:
            cls._instance = super(Singleton, cls).__new__(cls, *args, **kwargs)
        cls._instance.x = 10
        return cls._instance

a.py:

from singleton import Singleton
uvw = Singleton()
print(id(uvw))
uvw.x += 10
print(uvw.x)

b.py:

from singleton import Singleton
uvw = Singleton()
print(id(uvw))
uvw.x += 10
print(uvw.x)

main.py

import a
import b

我真的不想再进一步分割我的程序...那对我来说算是最后的手段了 :) - dom0
如果你的代码需要从另一个文件导入主模块,那么可能存在一些可疑的情况。 - Eric
看到这个重复的问题,@Eric;sys.modules['__main__']确实与sys.modules['x']不同。 - Martijn Pieters
@csde_rats 如果可能的话,将该单精度数放入“x”模块中。 - glglgl

7
Python每次只会按照名称加载模块一次(除非调用了reload(module))。 如果运行main.py,那么模块就是__main__(尝试打印uvw.__class__.__module__)。 当x导入main时,名为main的模块首次被加载。
如果您在第三个模块中或者在x中定义了uvw,只要以相同方式导入到__main__x中,它就是相同的对象。

1
我发现问题在于当从命令行执行时,main会被加载两次,一次作为__main__,另一次由x作为main导入时。
我找到了一个非常恶劣的黑客方法来规避第二次加载:
sys.modules["main"] = sys.modules["__main__"]

在我的情况下,将主模块和单例类分离并不可取。


3
为了避免恶意黑客攻击,你可以在一个不被执行为脚本的模块中定义 uvw,然后只需在主脚本中导入该模块即可。 - jfs

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接