【问题标题】:Implementing Singleton as metaclass, but for abstract classes将单例实现为元类,但用于抽象类
【发布时间】:2016-01-26 15:12:45
【问题描述】:

我有一个抽象类,我想为从我的抽象类继承的所有类实现单例模式。我知道我的代码不会工作,因为会有元类属性冲突。任何想法如何解决这个问题?

from abc import ABCMeta, abstractmethod, abstractproperty

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 GenericLogger(object):
    __metaclass__ = ABCMeta

    @abstractproperty
    def SearchLink(self): pass

class Logger(GenericLogger):
    __metaclass__ = Singleton

    @property
    def SearchLink(self): return ''

a = Logger()

【问题讨论】:

  • 单例背后的原理是什么?
  • 我将创建不同网站的解析器,每个解析器必须实现相同的方法,所以我想要一个抽象的基础。此外,每个网站我只需要 1 个解析器,不需要不同的解析器,因为解析器永远不会存储任何数据。它只处理内容。
  • 如果没有状态,为什么不只是一个具有功能的模块。或者使用一个类,但只做一个。如果有两个实例,这不是灾难性的,为什么需要强制执行?
  • 好吧,也许我并不完全诚实。我忘了添加解析器存储了一些特定于站点的信息,这些信息需要很长时间才能废弃,所以我认为强制程序员只能启动一次是有意义的。另外我想学习如何准确地实现单例
  • 强迫程序员不是一个好主意,除非有真正的好处,或者没有其他安全的方法。只要有一个函数来创建一个解析器和一个存储解析器的映射

标签: python singleton abstract-class metaclass abc


【解决方案1】:

创建ABCMeta的子类:

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


class GenericLogger(object):
    __metaclass__ = SingletonABCMeta

    @abstractproperty
    def SearchLink(self): pass


class Logger(GenericLogger):  
    @property
    def SearchLink(self): return ''

元类和普通类一样工作;您仍然可以创建子类并扩展它们的功能。 ABCMeta 本身并没有定义 __call__ 方法,所以添加一个是安全的。

演示:

>>> from abc import ABCMeta, abstractproperty
>>> class SingletonABCMeta(ABCMeta):
...     _instances = {}
...     def __call__(cls, *args, **kwargs):
...         if cls not in cls._instances:
...             cls._instances[cls] = super(SingletonABCMeta, cls).__call__(*args, **kwargs)
...         return cls._instances[cls]
...
>>> class GenericLogger(object):
...     __metaclass__ = SingletonABCMeta
...     @abstractproperty
...     def SearchLink(self): pass
...
>>> class Logger(GenericLogger):
...     @property
...     def SearchLink(self): return ''
...
>>> Logger()
<__main__.Logger object at 0x1012ace90>
>>> Logger()
<__main__.Logger object at 0x1012ace90>
>>> class IncompleteLogger(GenericLogger):
...     pass
...
>>> IncompleteLogger()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in __call__
TypeError: Can't instantiate abstract class IncompleteLogger with abstract methods SearchLink

【讨论】:

  • 我打算使用这个配方,唯一让我停下来的是以下问题: 1)假设我必须重新初始化一个 Singleton 对象,例如某种当下面的配置文件更改时配置单例。我怎么能做到这一点?在元类中添加de_init 方法是否有意义? 2) 另外,我们如何重新初始化一个由其他单例组成的单例?
  • @zapstar:实例存储在SingletonABCMeta._instances,因此您可以从那里清除它们。但是,如果您的配置需要更新,我只会使用模块全局来存储配置,而不是在这里使用单例模式。
  • 如果我使用模块全局方法,那么我将不得不提供一个初始化方法来加载配置,如果我使用单例,这是可以避免的。
  • @zapstar:你会使用一个函数:module.config()。然后该函数使用全局缓存。
  • 我在stackoverflow.com/a/6798042/1084879 上阅读了@agf 的答案,当我有数据接收器而不是数据源(例如全局配置)时,单例更好是有道理的
猜你喜欢
  • 2013-05-13
  • 1970-01-01
  • 2013-03-07
  • 2014-08-18
  • 2011-01-28
  • 2011-10-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多