【问题标题】:Using Singleton Classes in Multiple Modules in Python 3在 Python 3 的多个模块中使用单例类
【发布时间】:2018-08-31 09:00:56
【问题描述】:

这可能是一个愚蠢的问题,因为我刚刚了解了元类......

如果您在自己的文件中有 2 个 Singleton 类,那么在两个文件中复制 Singleton 元类定义是不好的做法,例如

Apple.py

class Singleton(type):
    _instances = {}
    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
        return cls._instances[cls]

class Apple(metaclass=Singleton):
    pass

print(Apple() == Apple())   # True

Orange.py

class Singleton(type):
    _instances = {}
    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
        return cls._instances[cls]

class Orange(metaclass=Singleton):
    pass

print(Orange() == Orange()) # True

或者我们应该将 Singleton 元类代码移动到它自己的文件Singleton.py 中,然后将它们导入到Apple.py 和Orange.py 中?

或者使用 singleton-decorator 包中的 @singleton 装饰器?

【问题讨论】:

  • is it poor practice to duplicate the Singleton metaclass definition in both files, like 是的。重复自己从来都不是一件好事。
  • @sytech 谢谢,我还没有遇到从另一个文件导入元类定义的代码。
  • @Nyxynyx 这正是标准库中的abc 模块所做的。元类在某些方面很神奇,但不是在这方面。
  • @chepner 我在这里有点困惑。您是否建议让Singleton 类成为ABCMeta 的子类而不是type?我不熟悉abc 模块,希望能得到解释
  • 为什么不会你把Singleton放在它自己的模块中?不要复制和粘贴代码。那是错的。

标签: python python-3.x singleton metaclass


【解决方案1】:

实际上,使用元类来定义单例是一种愚蠢的做法。我不知道最初是谁提出了这种模式,现在它很普遍,而且有点矫枉过正。

也就是说,只是为了快速回答:当然,复制代码是没有意义的 - 更不用说当你甚至不会更改单个参数时 - 它是完全相同的代码。只需将其保存在一个文件中并从另一个文件中导入,即使您不想为此创建特定文件。

现在,元类是 Python 中的一项强大功能,最好在知道发生了什么时使用。否则,会产生副作用,其中最常见的是无法将您的类与其他具有元类的类组合(这实际上可能是必要的)。

也就是说,这是一个基于装饰器的非元类单例模式:

_register = {}

def singleton(cls):
   def wrapper(*args, **kw):
       if cls not in _register:
           instance = cls(*args, **kw)
           _register[cls] = instance
       return _register[cls] 

   wrapper.__name__ = cls.__name__
   return wrapper

...
@singleton
class Orange:
    ...

上面的代码是按原样提供的,但如果使用它,请注意它不会警告有人试图创建具有不同参数的第二个实例。可以将其更改为不允许参数,或者在重新实例化时引发错误。

更好的是,装饰器模式可以在类声明时创建单例实例,这与元类不同。然后,​​甚至不需要寄存器,因为类将无法用于尝试产生不正确的第二个实例(尽管可以这样做 type(Orange)() - 如果您在一个项目中,这是一个谁可以破坏代码的上下文:

def singleton(cls):
      return cls()

就是这样。

@singleton
class Orange:
    ...

执行后,这会将“Orange”添加到命名空间,作为声明的“Orange”类的唯一允许实例。或者就此而言,只需执行以下操作:

class Orange:
   ...

Orange = Orange()

(瞧——你甚至解决了跨模块共享代码的问题——根本不需要额外的代码)

【讨论】:

    猜你喜欢
    • 2012-06-20
    • 2013-12-20
    • 1970-01-01
    • 2011-02-07
    • 1970-01-01
    • 1970-01-01
    • 2019-10-07
    • 2017-01-19
    • 1970-01-01
    相关资源
    最近更新 更多