我有一个Python应用程序,可以执行任务的工作流程。这些任务中的每一个都可能位于自己的模块中。完成所有任务的列表后,应用程序将关闭。在它关闭之前,我想从每个任务中收集相关的统计数据。
我在考虑使用单例模式提供一个位置来存储所有这些数据,以便我可以在最后检索它。每个任务都将导入singleton stats-tracking类,创建一个实例(该类的常见单个实例)并使用它来存储任何数据。
在我的情况下,我想要一个包来存储每个任务的数据。我一直听说单身人士很糟糕。我想获得有关使用单例设计模式或任何其他建议的输入。
答案 0 :(得分:2)
单身是否是"坏"或不似乎是品味问题。当然他们有自己的位置,单身主题的任何变化都适合你。
"博格模式" (格言不那么多,而且很少见," StatelessProxy"或者#34; Monostate")自从Alex Martelli聪明的ActiveState食谱{{3 }}。它与Singleton的不同之处在于允许类的多个不同对象,所有对象都共享公共数据。相反,Singleton模式确保只创建一个类的一个实例。
可以在此stackoverflow帖子中找到关于Borg vs Singleton问题的讨论:Singleton? We don't need no stinkin' singleton: the Borg design pattern。由于缺少_init_default_register
方法,帖子顶部的实现可能令人费解,其目的是仅创建和初始化公共数据属性。供参考和比较,这里是完整的实现(在Python 3中),它们都创建了一个数据属性(一个名为data
的dict):
在Python中实现Singleton的标准方法是使用元类;
class Singleton(type):
def __init__(cls, *args, **kwargs):
cls.__instance = None
super().__init__(*args, **kwargs)
def __call__(cls, *args, **kwargs):
if cls.__instance is None:
cls.__instance = super().__call__(*args, **kwargs)
return cls.__instance
您可以通过为其提供此元类来使您的统计跟踪类成为单例:
class StatsTrackerSingleton(metaclass=Singleton):
def __init__(self):
self.data = {}
# ... methods to update data, summarize it, etc. ...
Borg模式实现起来更简单,因为它没有使用元类,可能更灵活:
class StatsTrackerBorg():
__shared_data = {'data':{}}
# RHS guarantees a common dict named `data`
def __init__(self):
"""Make every instance use StatsTrackerBorg.__shared_data
for its attribute dict."""
self.__dict__ = self.__shared_data
# ... methods to update data, summarize it, etc. ...
使用上面实现的两种模式,您可以使用公共字典data
,您可以使用点运算符简单地获取和设置共享属性。例如,使用Borg类:
>>> a = StatsTrackerBorg()
>>> b = StatsTrackerBorg()
>>> a is b # would be True for StatsTrackerSingleton
False
>>> vars(a) is vars(b)
True
>>> vars(a)
{'data': {}}
>>> a.data['running_time'] = 10000
>>> b.bar = 10
>>> vars(a)
{'data': {'running_time': 10000}, 'bar': 10}
>>> b.foo = 'y'
>>> a.foo
'y'
两种模式之间的一个值得注意的差异:一个" Borg' ed"的子类。 class与超类共享相同的公共状态,并且仍然可以添加其实例可访问的更多共享状态,而Singleton类的每个子类获得其自己的唯一实例,因此它自己的公共状态与超类的不相交。对于某些预期的应用程序,其中一种行为可能显然比另一种行为更合适。