在 Python 中使用模块作为单例 - 可以吗?

Kow*_*weł 8 python singleton module class

我有一个非常复杂的单例对象。我决定修改它,所以它将是一个单独的模块,带有用于存储数据的模块范围的全局变量。

这种方法有什么缺陷吗?我只是觉得,这有点像 hacky,而且可能有一些我现在看不到的问题。也许有人这样做或有一些意见:) 在此先感谢您的帮助。问候。

// 最小、完整和可验证的示例:

"""

This is __init__.py of the module, that could be used as a singleton:

I need to set and get value of IMPORTANT_VARIABLE from different places in my code.

Folder structure:
--singleton_module
 |
 -__init__.py

Example of usage:
import singleton_module as my_singleton
my_singleton.set_important_variable(3)
print(my_singleton.get_important_variable())

"""

IMPORTANT_VARIABLE = 0

def set_important_variable(value):
    global IMPORTANT_VARIABLE
    IMPORTANT_VARIABLE = value

def get_important_variable():
    return IMPORTANT_VARIABLE
Run Code Online (Sandbox Code Playgroud)

bru*_*ers 14

从技术上讲,Python 模块是单例,因此从这个角度来看,您的代码没有特别的问题(除了单例的常见问题)。我只是在 all_lower 中拼写变量(ALL_UPPER 表示伪常量),并在它前面加上一个单(“受保护”)或双(“真正私有”)前导下划线,以表明它不是公共 API 的一部分(标准 Python 命名约定)。

现在单身人士是否是一个好主意是另一场辩论,但这不是这里的重点......

例如,在一种可能的情况下,我可能会丢失数据,或者该模块可以在代码的不同位置导入两次,因此如果在函数范围内导入或类似的东西,它就不会是单例。

一个模块每个进程只实例化一次(第一次导入),然后后续的导入将直接从sys.modules. 您可以拥有同一模块的两个不同实例的唯一情况是当模块通过两个不同的路径导入时,只有在您有一些损坏的情况下才会发生这种情况,sys.path即像这样:

src/
  foo/
    __init.py
    bar/
      __init__.py
      baaz/
         __init__.py
         mymodule.py
Run Code Online (Sandbox Code Playgroud)

使用 "src" 和 "foo" in sys.path,然后导入mymodule一次 asfrom foo.bar.baaz import mymodule和第二次 asfrom bar.baaz import mymodule

毋庸置疑,这是一个退化的案例,但它可能会发生并导致难以诊断的错误。请注意,当您遇到这种情况时,您确实会遇到很多其他问题,例如身份测试来自mymodule.

另外,我不确定使用对象而不是模块会如何提高安全性

它没有。

我只是问,如果这不是一个不好的做法,也许有人这样做并发现了一些问题。这可能不是流行的模式

好吧,恰恰相反,您经常会发现建议将模块用作单例,而不是使用只有静态方法、类方法和类属性的类(另一种在 Python 中实现单例的方法)。这通常涉及用作命名空间的无状态类,而您的示例确实有状态,但这并没有太大的实际区别。

现在你不会得到所有好的 OO 特性,比如计算属性、继承、魔法方法等,但我假设你已经理解了这一点。

就我而言,根据上下文,我可能宁愿使用普通类,但只将该类的一个实例公开为模块的 API,即:

# mymodule.py

__all__ = ["mysingleton"]

class __MySingletonLike(object):
    def __init__(self):
        self._variable = 42

    @property
    def variable(self):
        return self._variable

    @variable.setter
    def variable(self, value):
        check_value(value) # imaginary validation
        self._variable = value


mysingleton = __MySingleton()
Run Code Online (Sandbox Code Playgroud)

但这只是当我对类有特别关注时(实现重用、适当的可测试性、其他需要类的特殊功能等)。